Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
Easy, reusable code with this helper module
-
I found myself regularly wanting to copy modules I had already written into new projects. Things like Dice classes, confirmation views, or even a reusable color picker view. I wasn't about to put everything I thought was useful or reusable in the site-packages. I was aware, however, of the ability to add a directory to the
sys.path
so I could reference and import it, but in an app on iOS, this full path is very long and gross, and trying to use relative paths instead was almost equally undesirable. And what if the module I made gets renamed or moved? Now all those hard-coded references to its path in other modules need to be found and updated.I wrote this little helper module
sharedlibs.py
to simplify the process and dropped it in the site-packages folder.''' example use of this module: import sharedlibs sharedlibs.add_path_for('animated_image_view') from animated_image_view import AnimatedImageView ''' import os import sys def get_app_root(): # Returns the fully-qualified "root" directory of the Pythonista 3 app end = 'Pythonista3/Documents' cwd = os.getcwd() return cwd.split(end)[0] + end def add_path_for(*modules): # Adds the directory for the given library name to the search path so its contents can be imported. _libs = _load_libs_dict() for module in modules: if module in _libs: sys.path.append(get_app_root() + _libs[module]) else: raise Exception('No such shared module: .%s' % module) # Prints a list of accessible libraries def list(): _libs = _load_libs_dict() print('Available Shared Libraries:') for key in _libs: print(' %s: %s' % (key, _libs[key])) def _load_libs_dict(): d = {} print(__file__) libs_path = '%s/sharedlibs_list.txt' % '/'.join(__file__.split('/')[:-1]) with open(libs_path) as f: lines = [l.strip() for l in f.readlines()] for line in lines: if line != '' and not line.startswith('#'): k,v = line.split('=') d[k] = v return d if __name__ == '__main__': # just print the available libraries list()
This module and a
sharedlibs_list.txt
file were both added to the site-packages and nothing else ever again.
Thesharedlibs_list.txt
is simply a key-value pair file containing the short names of the libraries mapped to their paths relative to the Pythonista's app root. Something like this:animated_image_view=/UI/Views/AnimatedImageView brightness=/Utils/ColorBrightness config_file=/Utils/ConfigFile img_utils=/Utils/ImageUtils perm_utils=/Utils/PermutationUtils ui_drag=/UI/Draggable view_swap=/UI/ViewSwap word_utils=/Utils/WordUtils
You'll notice you don't need the full path from your iOS device's actual root; the
sharedlibs
module treats the Pythonista3/Documents directory as your root since you can't really access much outside there anyway.
Once Pythonista is restarted, the listed libraries will be accessible via thesharedlibs.add_path_for()
function, which will add that library's directory to the search path.One single, clean place to define the locations of your reusable libraries for easy import anywhere.
-
list is a builtin in Python so it is a bad idea to reuse that as a function name.
def shared_libraries(): print('Available Shared Libraries:') print(‘\n’.join(f' {k}: {v}’ for k, v in _load_libs_dict().items()))
-
Good call. That’s a good fix.
-
@iAmMortos, interesting. I just use
site-packages
, but does your method have the feature of reloading these imports every time? That is useful (if not very performant) when you end up needing to go back to add/fix something. -
It’s the same as adding a directory to your search path. So nothing is cached or saved or in need of reloading. The only thing that changes is when you add entries to the
sharedlibs_list.txt
file (which is on my favorites list for easy editing), which doesn’t require anything to be reloaded either since it’s not read until one of the functions in thesharedlibs
module is actually invoked. No clutter in the site-packages :) -
I really like this idea.
I’ve got this set up and put both the
sharededlibs.py
file and the sharedlibs_list.txt file in the generic site-packages directory. I’m able to run thesharedlibs.py
script directly and get it to list the “packages” I have put into the file.When I try to to actually use it now, I get an error:
stash: <class 'ModuleNotFoundError'>: No module named 'my_cmd'
I have a directory called “my_cmd” (and it is the path I use in the txt file above) with a
__init__.py
andBaseCmd.py
file in it.So, am I missing something? Probably am, but not seeing it! :-D Any help would be appreciated!
Thanks!
Mike -
Yeesh, I need to keep a better eye out for emails from this site.
Once you do the
add_path_for
, are you performing the actual import formy_cmd
?import sharedlibs sharedlibs.add_path_for('my_cmd') import my_cmd
As it works presently, my module doesn't do any importing, it just adds the predefined path in
shared_libs.txt
-
I suppose it'd be a nice addition to have some kind of
add_path_and_import
option as well. I've actually not done programmatic importing before.