1

I am building a python library. The functions I want available for users are in stemmer.py. Stemmer.py uses stemmerutil.py

I was wondering whether there is a way to make stemmerutil.py not accessible to users.

directory's snapshot

  • what do you mean with "not accessible"? do you want functions/classes/names defined in there to not be accessible to people who have installed your library through `urdu.stemmerUtil.*`, or do you mean that the file itself should be impossible to navigate to and update on a user's filesystem? – Arne Aug 20 '20 at 13:03

1 Answers1

2

If you want to hide implementation details from your users, there are two routes that you can go. The first uses conventions to signal what is and isn't part of the public API, and the other is a hack.


The convention for declaring an API within a python library is to add all classes/functions/names that should be exposed into an __all__-list of the topmost __init__.py. It doesn't do that many useful things, its main purpose nowadays is a symbolic "please use this and nothing else". Yours would probably look somewhat like this:

urdu/urdu/__init__.py

from urdu.stemmer import Foo, Bar, Baz
__all__ = [Foo, Bar, Baz]

To emphasize the point, you can also give all definitions within stemmerUtil.py an underscore before their name, e.g. def privateFunc(): ... becomes def _privateFunc(): ...


But you can also just hide the code from the interpreter by making it a resource instead of a module within the package and loading it dynamically. This is a hack, and probably a bad idea, but it is technically possible.

First, you rename stemmerUtil.py to just stemmerUtil - now it is no longer a python module and can't be imported with the import keyword. Next, update this line in stemmer.py

import stemmerUtil

with

import importlib.util
import importlib.resources  
# in python3.7 and lower, this is importlib_resources and needs to be installed first


stemmer_util_spec = importlib.util.spec_from_loader("stemmerUtil", loader=None)
stemmerUtil = importlib.util.module_from_spec(stemmer_util_spec)

with importlib.resources.path("urdu", "stemmerUtil") as stemmer_util_path:
    with open(stemmer_util_path) as stemmer_util_file:
        stemmer_util_code = stemmer_util_file.read()
exec(stemmer_util_code, stemmerUtil.__dict__)

After running this code, you can use the stemmerUtil module as if you had imported it, but it is invisible to anyone who installed your package - unless they run this exact code as well.

But as I said, if you just want to communicate to your users which part of your package is the public API, the first solution is vastly preferable.

Arne
  • 17,706
  • 5
  • 83
  • 99