Skip to content

Commit d785b6b

Browse files
committed
1.3.0 - The init file has been improved so as not to export symbols from other packages. Fixes #6
1 parent 5939323 commit d785b6b

File tree

5 files changed

+102
-5
lines changed

5 files changed

+102
-5
lines changed

docs/changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
### 1.3.0 - fixed init.py
2+
3+
* The init file has been improved so as not to export symbols from other packages. Fixes [#6](https://github.com/smarie/python-mini-lambda/issues/6)
4+
15
### 1.2.4 - Minor improvements in generated goodies
26

37
* Removed annoying warning message when loading goodies

mini_lambda/__init__.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
1-
# allow users to do
2-
# from mini_lambda import xxx
1+
from mini_lambda.utils_init import __remove_all_external_symbols, __get_all_submodules_symbols
2+
3+
__PACKAGE_NAME = 'mini_lambda'
4+
__SUBMODULES_TO_EXPORT = ['base', 'generated', 'generated2', 'goodies', 'goodies_generated', 'main']
5+
# TODO we could rather rely on a regexp mechanism
6+
7+
# (1) allow users to do
8+
# import <package> as p and then p.<symbol>
9+
__all__ = __get_all_submodules_symbols(__PACKAGE_NAME, __SUBMODULES_TO_EXPORT)
10+
# Note: this is one way to do it, but it would be simpler to check the names in globals() at the end of this file.
11+
12+
# (2) allow users to do
13+
# from <package> import <symbol>
14+
#
15+
# The following works, but unfortunately IDE like pycharm do not understand
316
from mini_lambda.base import *
417
from mini_lambda.generated import *
518
from mini_lambda.goodies_generated import *
@@ -8,6 +21,13 @@
821
from mini_lambda.generated2 import *
922
from mini_lambda.goodies import *
1023

11-
# allow users to do
12-
# import mini_lambda as v
13-
__all__ = ['base', 'generated', 'goodies_generated', 'main', 'generated2', 'goodies']
24+
# remove all symbols that were imported above but do not belong in this package
25+
__remove_all_external_symbols(__PACKAGE_NAME, globals())
26+
27+
# Otherwise exhaustive list would be required, which is sad
28+
# ...
29+
30+
# print(__all__)
31+
# print(globals().keys())
32+
# print('Done')
33+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
def test_named_import():
3+
import mini_lambda as ml
4+
o = ml.InputVar
5+
6+
7+
def test_import_from():
8+
from mini_lambda import InputVar
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from mini_lambda import * # this relies on '__all__'
2+
3+
4+
def test_wild_import():
5+
o = InputVar

mini_lambda/utils_init.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
def __is_defined_in_submodule(submodule_name_prefix, x):
2+
"""
3+
Utility method to return True if x is not a module and its module name starts with submodule_name_prefix
4+
e.g. 'autoclass.autohash_'.
5+
6+
:param submodule_name_prefix:
7+
:param x:
8+
:return:
9+
"""
10+
# if _inspect.ismodule(x):
11+
# return False
12+
# else:
13+
from inspect import getmodule
14+
m = getmodule(x)
15+
if m is None:
16+
return True
17+
elif hasattr(m, '__name__') and m.__name__.startswith(submodule_name_prefix):
18+
return True
19+
else:
20+
return False
21+
22+
23+
def __get_all_submodules_symbols(pkg_name, submodules_to_export):
24+
"""
25+
Generates the list of symbol names that can be used in the `__all__` variable in init.py
26+
The list is created from a list of submodules.
27+
28+
All symbols in these submodules that are not private and that are actually defined in there, get in the list.
29+
The submodules themselves end up in the list.
30+
31+
Note that this function should only be used if you also actually import those symbols in the init.py, so that they
32+
are actually visible at package root level.
33+
34+
:param submodules_to_export: a list of submodule names to export
35+
:return:
36+
"""
37+
from inspect import getmembers
38+
from copy import copy
39+
from importlib import import_module
40+
41+
# first create a copy of the submodules list
42+
all_ = copy(submodules_to_export)
43+
44+
# then for each submodule add the symbols that are declared in this submodule
45+
for submodule in submodules_to_export:
46+
submodule_full_name = pkg_name + '.' + submodule
47+
imported_module = import_module(submodule_full_name)
48+
# print(imported_module.__name__)
49+
for x_name, symbol in getmembers(imported_module):
50+
if not x_name.startswith('_'):
51+
if __is_defined_in_submodule(submodule_full_name, symbol):
52+
# print('{} is exported'.format(x_name))
53+
all_.append(x_name)
54+
return all_
55+
56+
57+
def __remove_all_external_symbols(pkg_name, globs):
58+
for x_name in list(globs.keys()):
59+
if not __is_defined_in_submodule(pkg_name, globs[x_name]):
60+
del globs[x_name]

0 commit comments

Comments
 (0)