Reputation: 857
I am having kind of a chicken or the egg problem with Python setuptools.
What I am trying to achieve, is to distribute a configuration file with my pip package (which in itself is perfectly possible with data_files
parameter in setup.py
) to OS specific common locations for user configuration files (e.g. ~/.config
on Linux).
I figured out that OS "specificity" can be solved using appdirs
[1] PyPi package. And there is my problem - the appdirs
is not guaranteed to be installed when installing my own package since it is a dependency of my package and thus installed after it (promised chicken or the egg :) )
My setup.py
contains something like this:
from setuptools import setup
from appdirs import AppDirs
...
setup(
...
data_files=[
(AppDirs(name, author).user_config_dir, ['config/myconfig'])
],
...
)
Can this be solved without writing my own version of setuptools (allusion intended ;) )?
[1]: https://pypi.python.org/pypi/appdirs
Upvotes: 4
Views: 2697
Reputation: 3663
As I mentioned in my comment I would recommend distributing a generic copy of your file with your package and then copying it to the user's config dir at runtime if it does not exist.
This shouldn't be very hard and involves:
Using setuptools
's package_data
(instead of data_files
). This places the file where it is accessible at runtime using pkg_resources
, in the "correct" location for the specific OS
When the program runs, use appdirs
to look for a user-specific, locally installed file.
If it does not exist, use pkg_resources
to find the file and copy it to the location provided by appdirs
While I haven't done this, this process should work nicely for multiple OSes and environments and as a bonus, during development too due to how pkg_resources
works.
setup.py
In setup.py, you should make sure to include your data file for your package using package_data
:
setup(
# ...
data_files={
"my_package": [ "my_package.conf.dist"
}
# ...
)
import os.path
import pkg_resources
import appdirs
def main():
"""Your app's main function"""
config = get_config()
# ...
# ...
def get_config():
"""Read configuration file and return its contents
"""
cfg_dir = appdirs.user_config_dir('MyApplication')
cfg_file = os.path.join(cfg_dir, 'my_application.conf')
if not os.path.isfile(cfg_file):
create_user_config(cfg_file)
with open(cfg_file) as f:
data = f.read()
# ... probably parse the file contents here ...
return data
def create_user_config(cfg_file):
"""Create the user's config file
Note: you can replace the copying of file contents using shutil.copyfile
"""
source = pkg_resources.resource_stream(__name__, 'my_package.conf.dist')
with open(cfg_file, 'w') as dest:
dest.writelines(source)
I hope this clears up the usage of pkg_resources
and package_data
.
Upvotes: 6