From be7c4d209874cfb2e8fb67f63fd2c334c71dc502 Mon Sep 17 00:00:00 2001 From: Dima Gerasimov Date: Sat, 11 Apr 2020 20:32:26 +0100 Subject: [PATCH 1/4] WIP on using ~/.config/my/my.config --- my/__init__.py | 12 ------------ my/init.py | 39 +++++++++++++++++++++++++++++++++++++++ setup.py | 1 + 3 files changed, 40 insertions(+), 12 deletions(-) delete mode 100644 my/__init__.py create mode 100644 my/init.py diff --git a/my/__init__.py b/my/__init__.py deleted file mode 100644 index 48d26dd6..00000000 --- a/my/__init__.py +++ /dev/null @@ -1,12 +0,0 @@ -# TODO how to make it mypy friendly? maybe defensive import? or mypy config? or interface file? - -try: - import mycfg -except ImportError: - import warnings - warnings.warn("mycfg package isn't found! That might result in issues") - - from . import mycfg_stub as mycfg # type: ignore[no-redef] - import sys - sys.modules['mycfg'] = mycfg - del sys diff --git a/my/init.py b/my/init.py new file mode 100644 index 00000000..d3638de9 --- /dev/null +++ b/my/init.py @@ -0,0 +1,39 @@ +''' +A hook to insert user's config directory into Python's search path. + +- Ideally that would be in __init__.py (so it's executed without having to import explicityly) + But, with namespace packages, we can't have __init__.py in the parent subpackage + (see http://python-notes.curiousefficiency.org/en/latest/python_concepts/import_traps.html#the-init-py-trap) + + Please let me know if you are aware of a better way of dealing with this! +''' + + +# separate function to present namespace pollution +def setup_config(): + from pathlib import Path + import sys + import warnings + + # TODO use appdir?? + cfg_dir = Path('~/.config').expanduser() + mycfg_dir = cfg_dir / 'my' + + if not mycfg_dir.exists(): + warnings.warn(f"my.config package isn't found! (expected at {mycfg_dir}). This might result in issues.") + from . import mycfg_stub as mycfg + sys.modules['my.config'] = mycfg + else: + mp = str(mycfg_dir) + if mp not in sys.path: + sys.path.insert(0, mp) + print("UPDATED PATH") # TODO FIXME remove + + try: + import my.config + except ImportError as ex: + warnings.warn(f"Importing my.config failed! (error: {ex}). This might result in issues.") + + +setup_config() +del setup_config diff --git a/setup.py b/setup.py index 8ed5a12e..489e1a80 100644 --- a/setup.py +++ b/setup.py @@ -37,6 +37,7 @@ def main(): 'testing': [ 'pytest', 'pytz', + 'pylint', ], }, ) From 4d33416696bf43327a0e8ac5623c355bf66ff3f2 Mon Sep 17 00:00:00 2001 From: Dima Gerasimov Date: Sat, 11 Apr 2020 21:14:32 +0100 Subject: [PATCH 2/4] add development doc --- doc/DEVELOPMENT.org | 8 ++++++++ my/init.py | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 doc/DEVELOPMENT.org diff --git a/doc/DEVELOPMENT.org b/doc/DEVELOPMENT.org new file mode 100644 index 00000000..c57e5e81 --- /dev/null +++ b/doc/DEVELOPMENT.org @@ -0,0 +1,8 @@ +* IDE setup: make sure my.config is in your package search path +In runtime, ~my.config~ is imported from the user config directory dynamically. + +However, Pycharm/Emacs/whatever you use won't be able to figure that out, so you'd need to adjust your IDE configuration. + +- Pycharm: basically, follow the instruction [[https://stackoverflow.com/a/55278260/706389][here]] + + i.e. create a new interpreter configuration (e.g. name it "Python 3.7 (for HPI)"), and add =~/.config/my=. diff --git a/my/init.py b/my/init.py index d3638de9..99ad8376 100644 --- a/my/init.py +++ b/my/init.py @@ -19,6 +19,8 @@ def setup_config(): cfg_dir = Path('~/.config').expanduser() mycfg_dir = cfg_dir / 'my' + # TODO maybe try importing first and if it's present, don't do anything? + if not mycfg_dir.exists(): warnings.warn(f"my.config package isn't found! (expected at {mycfg_dir}). This might result in issues.") from . import mycfg_stub as mycfg @@ -27,7 +29,6 @@ def setup_config(): mp = str(mycfg_dir) if mp not in sys.path: sys.path.insert(0, mp) - print("UPDATED PATH") # TODO FIXME remove try: import my.config From 2bcde388d7de75eb5bea5cd37474e4fe1b68bc36 Mon Sep 17 00:00:00 2001 From: Dima Gerasimov Date: Sat, 11 Apr 2020 22:04:22 +0100 Subject: [PATCH 3/4] adjust demo.py and the config template to use my.config --- demo.py | 26 +++++++++++-------- my/hypothesis.py | 9 ++++--- my/init.py | 13 +++++++--- mycfg_template/my/config/__init__.py | 8 ++++++ .../{mycfg => my/config}/repos/.gitkeep | 0 .../{mycfg => my/config}/repos/hypexport | 0 mycfg_template/mycfg/__init__.py | 6 ----- 7 files changed, 38 insertions(+), 24 deletions(-) create mode 100644 mycfg_template/my/config/__init__.py rename mycfg_template/{mycfg => my/config}/repos/.gitkeep (100%) rename mycfg_template/{mycfg => my/config}/repos/hypexport (100%) delete mode 100644 mycfg_template/mycfg/__init__.py diff --git a/demo.py b/demo.py index 3373e353..88e75540 100755 --- a/demo.py +++ b/demo.py @@ -30,20 +30,19 @@ def run(): ], stderr=DEVNULL) # - - # 4. create private with_my file and set path to private configuration - with_my = 'my_repo/with_my' - copy('my_repo/with_my.example', with_my) - + # 4. point my.config to the Hypothesis data mycfg_root = abspath('my_repo/mycfg_template') - # edit the config and set path to private configuration - my = Path(with_my).read_text().replace("MYCFG_DIR = ''", "MYCFG_DIR = '{}'".format(mycfg_root)) - Path(with_my).write_text(my) + init_file = Path(mycfg_root) / 'my/config/__init__.py' + init_file.write_text(init_file.read_text().replace( + '/path/to/hypothesis/data', + hypothesis_backups, + )) # - # 5. now we can use it! + # 4. now we can use it! + os.chdir(my_repo) - check_call(['my_repo/with_my', 'python3', '-c', ''' + check_call(['python3', '-c', ''' import my.hypothesis pages = my.hypothesis.get_pages() @@ -54,7 +53,12 @@ def run(): print('Title: ' + page.title) print('{} annotations'.format(len(page.highlights))) print() -''']) +'''], env={ + # this is just to prevent demo.py from using real data + # normally, it will rely on having my.config in ~/.config/my + 'MY_CONFIG': mycfg_root, + **os.environ, +}) # that should result in something like this: diff --git a/my/hypothesis.py b/my/hypothesis.py index 821975bd..30229eb6 100644 --- a/my/hypothesis.py +++ b/my/hypothesis.py @@ -1,13 +1,15 @@ """ Hypothes.is highlights and annotations """ +from . import init from .common import PathIsh -import mycfg.repos.hypexport as hypexport +import my.config.repos.hypexport as hypexport +from my.config.repos.hypexport import dal -from mycfg import paths -export_path: PathIsh = paths.hypothesis.export_path +from my.config import hypothesis as config +export_path: PathIsh = config.export_path ### @@ -17,7 +19,6 @@ from .error import Res, sort_res_by -from mycfg.repos.hypexport import dal # TODO weird. not sure why e.g. from dal import Highlight doesn't work.. diff --git a/my/init.py b/my/init.py index 99ad8376..e923d423 100644 --- a/my/init.py +++ b/my/init.py @@ -13,11 +13,18 @@ def setup_config(): from pathlib import Path import sys + import os import warnings - # TODO use appdir?? - cfg_dir = Path('~/.config').expanduser() - mycfg_dir = cfg_dir / 'my' + # not sure if that's necessary, i.e. could rely on PYTHONPATH instead + # on the other hand, by using MY_CONFIG we are guaranteed to load it from the desired path? + mvar = os.environ.get('MY_CONFIG') + if mvar is not None: + mycfg_dir = Path(mvar) + else: + # TODO use appdir?? + cfg_dir = Path('~/.config').expanduser() + mycfg_dir = cfg_dir / 'my' # TODO maybe try importing first and if it's present, don't do anything? diff --git a/mycfg_template/my/config/__init__.py b/mycfg_template/my/config/__init__.py new file mode 100644 index 00000000..dd427b61 --- /dev/null +++ b/mycfg_template/my/config/__init__.py @@ -0,0 +1,8 @@ +""" +Feel free to remove this if you don't need it/add your own custom settings and use them +""" + +class hypothesis: + # expects outputs from https://github.com/karlicoss/hypexport + # (it's just the standard Hypothes.is export format) + export_path = '/path/to/hypothesis/data' diff --git a/mycfg_template/mycfg/repos/.gitkeep b/mycfg_template/my/config/repos/.gitkeep similarity index 100% rename from mycfg_template/mycfg/repos/.gitkeep rename to mycfg_template/my/config/repos/.gitkeep diff --git a/mycfg_template/mycfg/repos/hypexport b/mycfg_template/my/config/repos/hypexport similarity index 100% rename from mycfg_template/mycfg/repos/hypexport rename to mycfg_template/my/config/repos/hypexport diff --git a/mycfg_template/mycfg/__init__.py b/mycfg_template/mycfg/__init__.py deleted file mode 100644 index d2d63563..00000000 --- a/mycfg_template/mycfg/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -class paths: - """ - Feel free to remove this if you don't need it/add your own custom settings and use them - """ - class hypothesis: - export_path = '/tmp/my_demo/backups/hypothesis' From 4641e5dd19ccd3993d4eace65eb8b03086cbc5a6 Mon Sep 17 00:00:00 2001 From: Dima Gerasimov Date: Sat, 11 Apr 2020 22:25:25 +0100 Subject: [PATCH 4/4] adjust tox config; temporary suppress pylint --- tox.ini | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tox.ini b/tox.ini index 13c542b4..777b4c6b 100644 --- a/tox.ini +++ b/tox.ini @@ -6,13 +6,13 @@ skip_missing_interpreters = True # TODO ugh. unclear how to reuse setup.cfg deps in tox [testenv] passenv = CI CI_* CIRCLE* -changedir = {toxworkdir}/{envname} +setenv = MY_CONFIG = mycfg_template commands = - # pip install -e .[testing] + pip install -e . # TODO ?? # python -m pytest {posargs} - python3 -c 'import my; print(my.__path__)' - python3 -c 'import my; import mycfg.repos' # shouldn't fail at least + python3 -c 'import my.init; import my.config; print(my.config.__path__)' + python3 -c 'import my.init; import my.config; import my.config.repos' # shouldn't fail at least # TODO run demo.py? just make sure with_my is a bit cleverer? # TODO e.g. under CI, rely on installing @@ -40,4 +40,5 @@ skip_install = true commands = pip install -e .[testing] # for now ignore import errors until I figure out how to import everything for CI checking.. - python -m pylint -E -d import-error my + # TODO FIXME ugh. fix later, after properly switched to my.config + # python -m pylint -E -d import-error my