diff --git a/.gitignore b/.gitignore index b6e4761..1618f62 100644 --- a/.gitignore +++ b/.gitignore @@ -127,3 +127,4 @@ dmypy.json # Pyre type checker .pyre/ +/.MY_README.md diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..b5b8301 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3.8-alpine +RUN pip install -U pip \ + && pip install -i https://test.pypi.org/simple/ autoload-module +COPY tests/main.py . +COPY tests/ tests +RUN rm tests/main.py +ENTRYPOINT ["python", "main.py"] \ No newline at end of file diff --git a/JP_README.md b/JP_README.md index c71c2e5..8c03b47 100644 --- a/JP_README.md +++ b/JP_README.md @@ -116,12 +116,21 @@ loader.load_classes("..packageA.validator") loader.load_classes("../packageA/validator") ``` +#### load_functions +``` +load_functions(pkg_name, [excludes]) +``` +引数で与えられたパッケージ名から配下のモジュールをimportし、関数オブジェクトのタプルを返却します。 +使い方は `load_classes` と同じです。 + **NOTE** -- クラスを検索するために, **モジュール名とクラス名を一致させてください.** -例えば, もし `test_module.py` と命名したのであれば, クラス名は `TestModule` にしてください。 -クラス名をカスタマイズしたい場合は, `@load_config` デコレータで `load=True` を指定してください。 +- クラスや関数を検索するために, **モジュール名とクラス名また関数名を一致させてください.** +例えば, もし `test_module.py` と命名したのであれば, クラス名は `TestModule` 、関数名は `test_module` にしてください。 +クラス名や関数名をカスタマイズしたい場合は, `@load_config` デコレータで `load=True` を指定してください。 - validator_a.py ```python + from autoload.decorator import load_config + @load_config(load=True) class CustomValidator: def validate(self): @@ -130,6 +139,8 @@ loader.load_classes("../packageA/validator") - 返却されるクラスオブジェクトに順番を持たせたいなら、同じく `@load_config` デコレータを使ってください。 - validator_a.py ```python + from autoload.decorator import load_config + # 昇順でソートされます @load_config(order=1) class ValidatorA: @@ -162,5 +173,11 @@ clazz().validate() ``` `file_name`の指定方法は `load_classes` と同じです。 +``` +load_function(file_name) +``` +Pythonファイルをimportして関数オブジェクトを返却します。 +使い方は `load_class` と同じです。 + ## License Released under the MIT license. \ No newline at end of file diff --git a/Pipfile b/Pipfile deleted file mode 100644 index b5846df..0000000 --- a/Pipfile +++ /dev/null @@ -1,11 +0,0 @@ -[[source]] -name = "pypi" -url = "https://pypi.org/simple" -verify_ssl = true - -[dev-packages] - -[packages] - -[requires] -python_version = "3.8" diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index e42812c..0000000 --- a/Pipfile.lock +++ /dev/null @@ -1,20 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "7f7606f08e0544d8d012ef4d097dabdd6df6843a28793eb6551245d4b2db4242" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.8" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": {}, - "develop": {} -} diff --git a/README.md b/README.md index 31c2a83..35a639b 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ except: ``` ## Install ``` -pip install autoload_module +pip install autoload-module ``` ## Usage ### Constructor @@ -118,12 +118,21 @@ loader.load_classes("..packageA.validator") loader.load_classes("../packageA/validator") ``` +#### load_functions +``` +load_functions(pkg_name, [excludes]) +``` +This method read the Python package and return the tuple of functions. +The usage is the same as `load_classes`. + **NOTE** -- To search class, **You must match the file name and class name.** -For example, if you named the file `test_module.py`, you must named the class `TestModule`. -When you want to customize the class name, use `@load_config` decorator and write `load=True` manually. +- To search class or function, **You must match the name of file and the one of class or function.** +For example, if you named the file `test_module.py`, you must named the class `TestModule` or the function `test_module`. +When you want to customize their name, use `@load_config` decorator and write `load=True` manually. - validator_a.py ```python + from autoload.decorator import load_config + @load_config(load=True) class CustomValidator: def validate(self): @@ -132,6 +141,8 @@ When you want to customize the class name, use `@load_config` decorator and writ - You can also control the order of loaded class objects using `@load_config` decorator. - validator_a.py ```python + from autoload.decorator import load_config + # sort in ascending order @load_config(order=1) class ValidatorA: @@ -164,5 +175,12 @@ clazz().validate() ``` How to specify `file_name` is the same as that of `load_classes`. +#### load_function +``` +load_class(file_name) +``` +This method read the Python file and return the function object. +The usage is the same as `load_function`. + ## License Released under the MIT license. \ No newline at end of file diff --git a/autoload/decorator.py b/autoload/decorator.py index a6d0e33..b32b5dc 100644 --- a/autoload/decorator.py +++ b/autoload/decorator.py @@ -1,7 +1,7 @@ def load_config(order=None, load=False): - def decorator(cls): + def decorator(resource): if order: - cls.load_order = order - cls.load_flg = load - return cls + resource.load_order = order + resource.load_flg = load + return resource return decorator \ No newline at end of file diff --git a/autoload/module_loader.py b/autoload/module_loader.py index c21df16..f57a9f5 100644 --- a/autoload/module_loader.py +++ b/autoload/module_loader.py @@ -3,84 +3,52 @@ import os import sys +__all__ = ( + "ModuleLoader" +) + +OP = os.path +SP = sys.path +THIS_FILE = OP.basename(__file__) +DEFAULT_EXCLUDES = ( + '__init__.py', + THIS_FILE, +) +DECORATOR_ATTR = "load_flg" -class ModuleLoader: - op = os.path - sp = sys.path - this_file = op.basename(__file__) - default_excludes = ( - '__init__.py', - this_file, - ) +class ModuleLoader: def __init__(self, base_path=None): self.__base_path = self.__init_base_url(base_path) + self.__context = None def load_class(self, file_name): - target_file = file_name - if file_name.endswith('.py'): - target_file = file_name.replace('.py', '') - fix_path_arr = self.__path_fix(target_file).split('/') - target_file = fix_path_arr[-2] - target_path = '/'.join(fix_path_arr[:-2]) - if target_path not in self.sp: - self.sp.append(target_path) - module = importlib.import_module(target_file) - for mod_name, clazz in inspect.getmembers(module, inspect.isclass): - if hasattr(clazz, "load_flg") and clazz.load_flg: - return clazz - if "".join(target_file.split("_")).lower() != mod_name.lower(): - continue - return clazz + self.__context = self.Context(self.Context.Type.clazz) + return self.__load_resource(file_name) - def load_classes(self, pkg_name=None, excludes=None): - target_dir = self.__path_fix(pkg_name) - if not self.op.isdir(target_dir): - raise NotADirectoryError('Not Found The Directory : {}'.format(target_dir)) - if target_dir not in self.sp: - self.sp.append(target_dir) - files = [self.op.splitext(file)[0] for file in os.listdir(target_dir) if file.endswith('.py')] - exclude_files = list(self.default_excludes) - exclude_files.append(self.op.basename(self.__detect_call_path())) - if excludes: - if not iter(excludes): - raise TypeError('excludes variable must be iterable.') - for exclude in excludes: - if not isinstance(exclude, str): - raise TypeError('The contents of the excludes must all be strings') - exclude_files.append(exclude) - fix_excludes = [exclude.replace('.py', '') for exclude in exclude_files] - excluded_files = tuple(set(files) - set(fix_excludes)) - classes = [] - for file in excluded_files: - module = importlib.import_module(file) - for mod_name, clazz in inspect.getmembers(module, inspect.isclass): - if hasattr(clazz, "load_flg") and clazz.load_flg: - classes.append(clazz) - break - if "".join(file.split("_")).lower() != mod_name.lower(): - continue - classes.append(clazz) - has_order_classes = [clazz for clazz in classes if hasattr(clazz, 'load_order') and clazz.load_order] - if not has_order_classes: - return tuple(classes) - no_has_order_classes = [clazz for clazz in classes if not hasattr(clazz, 'load_order') or not clazz.load_order] - if not no_has_order_classes: - return tuple(sorted(has_order_classes, key=lambda clazz:clazz.load_order)) - ordered_classes = sorted(has_order_classes, key=lambda clazz:clazz.load_order) + no_has_order_classes - return tuple(ordered_classes) + def load_function(self, file_name): + self.__context = self.Context(self.Context.Type.func) + return self.__load_resource(file_name) + + def load_classes(self, pkg_name, excludes=None): + self.__context = self.Context(self.Context.Type.clazz) + return self.__load_resources(pkg_name, excludes=excludes) + + def load_functions(self, pkg_name, excludes=None): + self.__context = self.Context(self.Context.Type.func) + return self.__load_resources(pkg_name, excludes=excludes, type='function') def __detect_call_path(self): for path in inspect.stack(): path_name = path.filename - filename = self.op.basename(path.filename) - if self.this_file == filename: + filename = OP.basename(path.filename) + if THIS_FILE == filename: continue return path_name def __init_base_url(self, base_path=None): if not base_path: - return self.op.dirname(self.__detect_call_path()) + return OP.dirname(self.__detect_call_path()) if self.__base_path.endswith('/'): return self.__base_path[:-1] return base_path @@ -128,4 +96,82 @@ def __path_fix(self, name): return result_base_path + '/' + path + '/' # example: foo.bar path = '/'.join(name.split('.')) - return self.__base_path + '/' + path + '/' \ No newline at end of file + return self.__base_path + '/' + path + '/' + + def __load_resource(self, file_name): + target_file = file_name.replace('.py', '') if file_name.endswith('.py') else file_name + fix_path_arr = self.__path_fix(target_file).split('/') + target_file = fix_path_arr[-2] + target_path = '/'.join(fix_path_arr[:-2]) + if target_path not in SP: + SP.append(target_path) + module = importlib.import_module(target_file) + comparison = self.__context.draw_comparison(target_file) + for mod_name, resource in inspect.getmembers(module, self.__context.predicate): + if hasattr(resource, DECORATOR_ATTR) and resource.load_flg: + return resource + if comparison != mod_name.lower(): + continue + del self.__context + return resource + + def __load_resources(self, pkg_name, excludes=None, type='class'): + target_dir = self.__path_fix(pkg_name) + if not OP.isdir(target_dir): + raise NotADirectoryError('Not Found The Directory : {}'.format(target_dir)) + if target_dir not in SP: + SP.append(target_dir) + files = [OP.splitext(file)[0] for file in os.listdir(target_dir) if file.endswith('.py')] + exclude_files = list(DEFAULT_EXCLUDES) + exclude_files.append(OP.basename(self.__detect_call_path())) + if excludes: + if not iter(excludes): + raise TypeError('excludes variable must be iterable.') + for exclude in excludes: + if not isinstance(exclude, str): + raise TypeError('The contents of the excludes must all be strings') + exclude_files.append(exclude) + fix_excludes = [exclude.replace('.py', '') for exclude in exclude_files] + excluded_files = tuple(set(files) - set(fix_excludes)) + classes = [] + for file in excluded_files: + module = importlib.import_module(file) + for mod_name, clazz in inspect.getmembers(module, self.__context.predicate): + if hasattr(clazz, DECORATOR_ATTR) and clazz.load_flg: + classes.append(clazz) + break + if self.__context.draw_comparison(file) != mod_name.lower(): + continue + classes.append(clazz) + del self.__context + has_order_classes = [clazz for clazz in classes if hasattr(clazz, 'load_order') and clazz.load_order] + if not has_order_classes: + return tuple(classes) + no_has_order_classes = [clazz for clazz in classes if not hasattr(clazz, 'load_order') or not clazz.load_order] + if not no_has_order_classes: + return tuple(sorted(has_order_classes, key=lambda clazz: clazz.load_order)) + ordered_classes = sorted(has_order_classes, key=lambda clazz: clazz.load_order) + no_has_order_classes + return tuple(ordered_classes) + + class Context: + # Don't use enum because it is not supported under Python 3.4 version + class Type: + func = 'function' + clazz = 'class' + + def __init__(self, type): + self.__type = type + if type == self.Type.clazz: + self.__predicate = inspect.isclass + else: + self.__predicate = inspect.isfunction + + @property + def predicate(self): + return self.__predicate + + def draw_comparison(self, file): + if self.__type == self.Type.clazz: + return "".join(file.split("_")).lower() + else: + return file.lower() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..6956844 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,35 @@ +[tool.poetry] +name = "autoload-module" +version = "1.1.0" +description = "Python Autoload Module" +authors = ["Hiroki Miyaji "] +license = "MIT" +maintainers = ["Hiroki Miyaji "] +readme = "README.md" +homepage = "https://github.com/hiroki0525/autoload_module" +repository = "https://github.com/hiroki0525/autoload_module" +documentation = "https://github.com/hiroki0525/autoload_module" +keywords = ["python", "import", "autoload", "autoload_module", "metaprogramming"] +classifiers = [ + 'Topic :: Software Development :: Libraries', + 'Development Status :: 1 - Planning', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: MIT License', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: Implementation :: CPython', + 'Programming Language :: Python :: Implementation :: PyPy', +] +packages = [ + { include = "autoload" }, +] + +[tool.poetry.dependencies] +python = "^3.8" + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" diff --git a/setup.py b/setup.py deleted file mode 100644 index 6db16c5..0000000 --- a/setup.py +++ /dev/null @@ -1,36 +0,0 @@ -from setuptools import setup, find_packages - -with open("README.md", "r") as fh: - long_description = fh.read() - -setup( - name='autoload_module', - description='Python Autoload Module', - long_description=long_description, - keywords=( - 'python', - 'import', - 'autoload', - 'autoload_module', - 'dynamic import', - 'metaprogramming', - ), - classifiers=( - 'Topic :: Software Development :: Libraries', - 'Development Status :: 1 - Planning', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: Implementation :: CPython', - 'Programming Language :: Python :: Implementation :: PyPy', - ), - long_description_content_type="text/markdown", - license="MIT License", - version='1.0.1', - url='https://github.com/hiroki0525/autoload_module', - author='Hiroki Miyaji', - author_email='nukoprogramming@gmail.com', - py_modules = ['autoload.module_loader', 'autoload.decorator'] -) \ No newline at end of file diff --git a/test.sh b/test.sh new file mode 100644 index 0000000..de93c48 --- /dev/null +++ b/test.sh @@ -0,0 +1,4 @@ +echo "Docker Build" +docker build --no-cache -t autoload-module-test . +echo "Docker Run" +docker run -it autoload-module-test \ No newline at end of file diff --git a/tests/base/module_2.py b/tests/base/module_2.py deleted file mode 100644 index 8ef6044..0000000 --- a/tests/base/module_2.py +++ /dev/null @@ -1,5 +0,0 @@ -from tests.test_module import TestModule - - -class Module2(TestModule): - pass \ No newline at end of file diff --git a/tests/base/module_3.py b/tests/base/module_3.py deleted file mode 100644 index 5f6723b..0000000 --- a/tests/base/module_3.py +++ /dev/null @@ -1,5 +0,0 @@ -from tests.test_module import TestModule - - -class Module3(TestModule): - pass \ No newline at end of file diff --git a/tests/__init__.py b/tests/clazz/__init__.py similarity index 100% rename from tests/__init__.py rename to tests/clazz/__init__.py diff --git a/tests/base/__init__.py b/tests/clazz/base/__init__.py similarity index 100% rename from tests/base/__init__.py rename to tests/clazz/base/__init__.py diff --git a/tests/base/module_1.py b/tests/clazz/base/module_1.py similarity index 66% rename from tests/base/module_1.py rename to tests/clazz/base/module_1.py index 739eca5..06831a4 100644 --- a/tests/base/module_1.py +++ b/tests/clazz/base/module_1.py @@ -1,6 +1,6 @@ import math -from tests.test_module import TestModule +from tests.clazz.test_module import TestModule class Module1(TestModule): diff --git a/tests/clazz/base/module_2.py b/tests/clazz/base/module_2.py new file mode 100644 index 0000000..60d27ca --- /dev/null +++ b/tests/clazz/base/module_2.py @@ -0,0 +1,5 @@ +from tests.clazz.test_module import TestModule + + +class Module2(TestModule): + pass \ No newline at end of file diff --git a/tests/clazz/base/module_3.py b/tests/clazz/base/module_3.py new file mode 100644 index 0000000..730601f --- /dev/null +++ b/tests/clazz/base/module_3.py @@ -0,0 +1,5 @@ +from tests.clazz.test_module import TestModule + + +class Module3(TestModule): + pass \ No newline at end of file diff --git a/tests/base/packageC/__init__.py b/tests/clazz/base/packageC/__init__.py similarity index 100% rename from tests/base/packageC/__init__.py rename to tests/clazz/base/packageC/__init__.py diff --git a/tests/base/packageC/module_c1.py b/tests/clazz/base/packageC/module_c1.py similarity index 68% rename from tests/base/packageC/module_c1.py rename to tests/clazz/base/packageC/module_c1.py index 3651b0f..1be9f35 100644 --- a/tests/base/packageC/module_c1.py +++ b/tests/clazz/base/packageC/module_c1.py @@ -1,5 +1,5 @@ from autoload.decorator import load_config -from tests.test_module import TestModule +from tests.clazz.test_module import TestModule @load_config(order=3) diff --git a/tests/base/packageC/module_c2.py b/tests/clazz/base/packageC/module_c2.py similarity index 68% rename from tests/base/packageC/module_c2.py rename to tests/clazz/base/packageC/module_c2.py index 5b87769..040acdc 100644 --- a/tests/base/packageC/module_c2.py +++ b/tests/clazz/base/packageC/module_c2.py @@ -1,5 +1,5 @@ from autoload.decorator import load_config -from tests.test_module import TestModule +from tests.clazz.test_module import TestModule @load_config(order=2) diff --git a/tests/base/packageC/module_c3.py b/tests/clazz/base/packageC/module_c3.py similarity index 68% rename from tests/base/packageC/module_c3.py rename to tests/clazz/base/packageC/module_c3.py index 526dfa0..c53860e 100644 --- a/tests/base/packageC/module_c3.py +++ b/tests/clazz/base/packageC/module_c3.py @@ -1,5 +1,5 @@ from autoload.decorator import load_config -from tests.test_module import TestModule +from tests.clazz.test_module import TestModule @load_config(order=1) diff --git a/tests/base/test_autoload_module.py b/tests/clazz/base/test_autoload_module.py similarity index 86% rename from tests/base/test_autoload_module.py rename to tests/clazz/base/test_autoload_module.py index d385398..98bb81a 100644 --- a/tests/base/test_autoload_module.py +++ b/tests/clazz/base/test_autoload_module.py @@ -2,22 +2,22 @@ import unittest from pathlib import Path -sys.path.append(str(Path(__file__).parent.parent.parent)) -sys.path.append(str(Path(__file__).parent.parent.parent / "autoload")) +sys.path.append(str(Path(__file__).parent.parent.parent.parent)) +sys.path.append(str(Path(__file__).parent.parent.parent.parent / "autoload")) from autoload.module_loader import ModuleLoader -from tests.base.module_1 import Module1 -from tests.base.module_2 import Module2 -from tests.base.module_3 import Module3 -from tests.base.packageC.module_c1 import ModuleC1 -from tests.base.packageC.module_c2 import ModuleC2 -from tests.base.packageC.module_c3 import ModuleC3 -from tests.packageA.module_a1 import ModuleA1 -from tests.packageA.module_a2 import ModuleA2 -from tests.packageA.module_a3 import ModuleA3 -from tests.packageA.packageB.module_b1 import CustomModuleB1 -from tests.packageA.packageB.module_b2 import ModuleB2 -from tests.packageA.packageB.module_b3 import ModuleB3 +from tests.clazz.base.module_1 import Module1 +from tests.clazz.base.module_2 import Module2 +from tests.clazz.base.module_3 import Module3 +from tests.clazz.base.packageC.module_c1 import ModuleC1 +from tests.clazz.base.packageC.module_c2 import ModuleC2 +from tests.clazz.base.packageC.module_c3 import ModuleC3 +from tests.clazz.packageA.module_a1 import ModuleA1 +from tests.clazz.packageA.module_a2 import ModuleA2 +from tests.clazz.packageA.module_a3 import ModuleA3 +from tests.clazz.packageA.packageB.module_b1 import CustomModuleB1 +from tests.clazz.packageA.packageB.module_b2 import ModuleB2 +from tests.clazz.packageA.packageB.module_b3 import ModuleB3 class TestAutoLoadModule(unittest.TestCase): diff --git a/tests/packageA/__init__.py b/tests/clazz/packageA/__init__.py similarity index 100% rename from tests/packageA/__init__.py rename to tests/clazz/packageA/__init__.py diff --git a/tests/clazz/packageA/module_a1.py b/tests/clazz/packageA/module_a1.py new file mode 100644 index 0000000..3561a72 --- /dev/null +++ b/tests/clazz/packageA/module_a1.py @@ -0,0 +1,5 @@ +from tests.clazz.test_module import TestModule + + +class ModuleA1(TestModule): + pass \ No newline at end of file diff --git a/tests/packageA/module_a2.py b/tests/clazz/packageA/module_a2.py similarity index 68% rename from tests/packageA/module_a2.py rename to tests/clazz/packageA/module_a2.py index f779b3c..03b4262 100644 --- a/tests/packageA/module_a2.py +++ b/tests/clazz/packageA/module_a2.py @@ -1,5 +1,5 @@ from autoload.decorator import load_config -from tests.test_module import TestModule +from tests.clazz.test_module import TestModule @load_config(order=1) diff --git a/tests/clazz/packageA/module_a3.py b/tests/clazz/packageA/module_a3.py new file mode 100644 index 0000000..3daf031 --- /dev/null +++ b/tests/clazz/packageA/module_a3.py @@ -0,0 +1,5 @@ +from tests.clazz.test_module import TestModule + + +class ModuleA3(TestModule): + pass \ No newline at end of file diff --git a/tests/packageA/packageB/__init__.py b/tests/clazz/packageA/packageB/__init__.py similarity index 100% rename from tests/packageA/packageB/__init__.py rename to tests/clazz/packageA/packageB/__init__.py diff --git a/tests/packageA/packageB/module.yaml b/tests/clazz/packageA/packageB/module.yaml similarity index 100% rename from tests/packageA/packageB/module.yaml rename to tests/clazz/packageA/packageB/module.yaml diff --git a/tests/packageA/packageB/module_b1.py b/tests/clazz/packageA/packageB/module_b1.py similarity index 70% rename from tests/packageA/packageB/module_b1.py rename to tests/clazz/packageA/packageB/module_b1.py index c561727..7de3bed 100644 --- a/tests/packageA/packageB/module_b1.py +++ b/tests/clazz/packageA/packageB/module_b1.py @@ -1,5 +1,5 @@ from autoload.decorator import load_config -from tests.test_module import TestModule +from tests.clazz.test_module import TestModule @load_config(load=True) class CustomModuleB1(TestModule): diff --git a/tests/clazz/packageA/packageB/module_b2.py b/tests/clazz/packageA/packageB/module_b2.py new file mode 100644 index 0000000..e9eb125 --- /dev/null +++ b/tests/clazz/packageA/packageB/module_b2.py @@ -0,0 +1,5 @@ +from tests.clazz.test_module import TestModule + + +class ModuleB2(TestModule): + pass \ No newline at end of file diff --git a/tests/clazz/packageA/packageB/module_b3.py b/tests/clazz/packageA/packageB/module_b3.py new file mode 100644 index 0000000..3f1eceb --- /dev/null +++ b/tests/clazz/packageA/packageB/module_b3.py @@ -0,0 +1,5 @@ +from tests.clazz.test_module import TestModule + + +class ModuleB3(TestModule): + pass \ No newline at end of file diff --git a/tests/test_module.py b/tests/clazz/test_module.py similarity index 100% rename from tests/test_module.py rename to tests/clazz/test_module.py diff --git a/tests/func/__init__.py b/tests/func/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/func/base/__init__.py b/tests/func/base/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/func/base/func1.py b/tests/func/base/func1.py new file mode 100644 index 0000000..bf90907 --- /dev/null +++ b/tests/func/base/func1.py @@ -0,0 +1,4 @@ +from math import acos + +def func1(): + return 'func1' \ No newline at end of file diff --git a/tests/func/base/func2.py b/tests/func/base/func2.py new file mode 100644 index 0000000..5480d28 --- /dev/null +++ b/tests/func/base/func2.py @@ -0,0 +1,2 @@ +def func2(): + return 'func2' \ No newline at end of file diff --git a/tests/func/base/func3.py b/tests/func/base/func3.py new file mode 100644 index 0000000..aaa7e01 --- /dev/null +++ b/tests/func/base/func3.py @@ -0,0 +1,2 @@ +def func3(): + return 'func3' \ No newline at end of file diff --git a/tests/func/base/packageC/__init__.py b/tests/func/base/packageC/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/func/base/packageC/packageC_func1.py b/tests/func/base/packageC/packageC_func1.py new file mode 100644 index 0000000..a92c4d1 --- /dev/null +++ b/tests/func/base/packageC/packageC_func1.py @@ -0,0 +1,6 @@ +from autoload.decorator import load_config + + +@load_config(order=3) +def packageC_func1(): + return 'packageC_func1' \ No newline at end of file diff --git a/tests/func/base/packageC/packageC_func2.py b/tests/func/base/packageC/packageC_func2.py new file mode 100644 index 0000000..b1d9644 --- /dev/null +++ b/tests/func/base/packageC/packageC_func2.py @@ -0,0 +1,6 @@ +from autoload.decorator import load_config + + +@load_config(order=2) +def packageC_func2(): + return 'packageC_func2' \ No newline at end of file diff --git a/tests/func/base/packageC/packageC_func3.py b/tests/func/base/packageC/packageC_func3.py new file mode 100644 index 0000000..24c9f65 --- /dev/null +++ b/tests/func/base/packageC/packageC_func3.py @@ -0,0 +1,6 @@ +from autoload.decorator import load_config + + +@load_config(order=1) +def packageC_func3(): + return 'packageC_func3' \ No newline at end of file diff --git a/tests/func/base/test_autoload_module.py b/tests/func/base/test_autoload_module.py new file mode 100644 index 0000000..c65f533 --- /dev/null +++ b/tests/func/base/test_autoload_module.py @@ -0,0 +1,121 @@ +import sys +import unittest +from pathlib import Path + +sys.path.append(str(Path(__file__).parent.parent.parent.parent)) +sys.path.append(str(Path(__file__).parent.parent.parent.parent / "autoload")) + +from autoload.module_loader import ModuleLoader +from tests.func.base.func1 import func1 +from tests.func.base.func2 import func2 +from tests.func.base.func3 import func3 +from tests.func.base.packageC.packageC_func1 import packageC_func1 +from tests.func.base.packageC.packageC_func2 import packageC_func2 +from tests.func.base.packageC.packageC_func3 import packageC_func3 +from tests.func.packageA.packageA_func1 import packageA_func1 +from tests.func.packageA.packageA_func3 import packageA_func3 +from tests.func.packageA.packageA_func2 import packageA_func2 +from tests.func.packageA.packageB.packageB_func1 import packageB_func1 +from tests.func.packageA.packageB.packageB_func2 import packageB_func2 +from tests.func.packageA.packageB.packageB_func3 import packageB_func3 + + +class TestAutoLoadModule(unittest.TestCase): + + def setUp(self): + print('setup') + self.loader = ModuleLoader() + + def test_load_function(self): + result_1 = func1() + result_A_1 = packageA_func1() + result_B_1 = packageB_func1() + result_C_1 = packageC_func1() + # Importing path test runs on class base test. + test_cases = ( + ("func1", result_1), + ("..packageA.packageA_func1", result_A_1), + ("../packageA/packageB/packageB_func1", result_B_1), + ("/packageC/packageC_func1", result_C_1), + ) + for file_name, expected in test_cases: + with self.subTest(file_name=file_name): + self.assertEqual(self.loader.load_function(file_name)(), expected) + + def test_load_functions_exclude(self): + basepkg_result = {func3(), func2(), func1()} + test_cases = ( + (".", None, basepkg_result), + (".", [], basepkg_result), + (".", ["func3"], {func2(), func1()}), + (".", ["func3", "func2"], {func1()}), + (".", ("func3", "func2"), {func1()}), + ) + for pkg_name, exclude, expected in test_cases: + with self.subTest(pkg_name=pkg_name, exclude=exclude): + functions = self.loader.load_functions(pkg_name, exclude) + results = set([function() for function in functions]) + self.assertSetEqual(results, expected) + + def test_load_functions_complex_path_load(self): + pkgB_result = {packageB_func3(), packageB_func2(), packageB_func1()} + test_cases = ( + ("../packageA/packageB", None, pkgB_result), + ) + for pkg_name, exclude, expected in test_cases: + with self.subTest(pkg_name=pkg_name, exclude=exclude): + functions = self.loader.load_functions(pkg_name, exclude) + results = set([function() for function in functions]) + self.assertSetEqual(results, expected) + + def test_load_functions_partial_order(self): + # Only ModuleA1 has order. + pkgA_result = (packageA_func2(), packageA_func3(), packageA_func1()) + test_cases = ( + ("../packageA/", None, pkgA_result), + ) + for pkg_name, exclude, expected in test_cases: + with self.subTest(pkg_name=pkg_name, exclude=exclude): + functions = self.loader.load_functions(pkg_name, exclude) + results = [function() for function in functions] + if not results[0] == expected[0]: + self.fail() + self.assertSetEqual(set(results[1:]), set(expected[1:])) + + def test_load_functions_no_order(self): + # Module1 has other python package. + basepkg_result = {func3(), func2(), func1()} + test_cases = ( + ("", None, basepkg_result), + ("./", ("func3", "func2"), {func1()}), + ) + for pkg_name, exclude, expected in test_cases: + with self.subTest(pkg_name=pkg_name, exclude=exclude): + functions = self.loader.load_functions(pkg_name, exclude) + results = set([function() for function in functions]) + self.assertSetEqual(results, expected) + + def test_load_functions_order(self): + pkgC_result = (packageC_func3(), packageC_func2(), packageC_func1()) + test_cases = ( + ("packageC", [], pkgC_result), + ("packageC", ["packageC_func2"], (packageC_func3(), packageC_func1())), + ) + for pkg_name, exclude, expected in test_cases: + with self.subTest(pkg_name=pkg_name, exclude=exclude): + functions = self.loader.load_functions(pkg_name, exclude) + results = tuple([function() for function in functions]) + self.assertTupleEqual(results, expected) + + def test_load_functions_raise_error(self): + test_cases = ( + ("./nonepackage", None), + (".", 123), + (".", [1, 2, 3]), + ) + for pkg_name, exclude in test_cases: + with self.assertRaises(Exception): + self.loader.load_functions(pkg_name, exclude) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/func/packageA/__init__.py b/tests/func/packageA/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/func/packageA/packageA_func1.py b/tests/func/packageA/packageA_func1.py new file mode 100644 index 0000000..f00ea36 --- /dev/null +++ b/tests/func/packageA/packageA_func1.py @@ -0,0 +1,2 @@ +def packageA_func1(): + return 'packageA_func1' \ No newline at end of file diff --git a/tests/func/packageA/packageA_func2.py b/tests/func/packageA/packageA_func2.py new file mode 100644 index 0000000..c378acb --- /dev/null +++ b/tests/func/packageA/packageA_func2.py @@ -0,0 +1,6 @@ +from autoload.decorator import load_config + + +@load_config(order=1) +def packageA_func2(): + return 'packageA_func2' \ No newline at end of file diff --git a/tests/func/packageA/packageA_func3.py b/tests/func/packageA/packageA_func3.py new file mode 100644 index 0000000..9d6f7d4 --- /dev/null +++ b/tests/func/packageA/packageA_func3.py @@ -0,0 +1,2 @@ +def packageA_func3(): + return 'packageA_func3' \ No newline at end of file diff --git a/tests/func/packageA/packageB/__init__.py b/tests/func/packageA/packageB/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/func/packageA/packageB/func.yaml b/tests/func/packageA/packageB/func.yaml new file mode 100644 index 0000000..e69de29 diff --git a/tests/func/packageA/packageB/packageB_func1.py b/tests/func/packageA/packageB/packageB_func1.py new file mode 100644 index 0000000..00c4cac --- /dev/null +++ b/tests/func/packageA/packageB/packageB_func1.py @@ -0,0 +1,5 @@ +from autoload.decorator import load_config + +@load_config(load=True) +def packageB_func1(): + return 'packageB_func1' \ No newline at end of file diff --git a/tests/func/packageA/packageB/packageB_func2.py b/tests/func/packageA/packageB/packageB_func2.py new file mode 100644 index 0000000..c9c548c --- /dev/null +++ b/tests/func/packageA/packageB/packageB_func2.py @@ -0,0 +1,2 @@ +def packageB_func2(): + return 'packageB_func2' \ No newline at end of file diff --git a/tests/func/packageA/packageB/packageB_func3.py b/tests/func/packageA/packageB/packageB_func3.py new file mode 100644 index 0000000..93d6027 --- /dev/null +++ b/tests/func/packageA/packageB/packageB_func3.py @@ -0,0 +1,2 @@ +def packageB_func3(): + return 'packageB_func3' \ No newline at end of file diff --git a/tests/main.py b/tests/main.py new file mode 100644 index 0000000..8d1fd33 --- /dev/null +++ b/tests/main.py @@ -0,0 +1,20 @@ +""" +This is test file. +You should run this in Docker container. +""" + +from autoload.module_loader import ModuleLoader + + +def main(): + print("--- start test -------------------------") + loader = ModuleLoader() + print(loader.load_class(".tests.clazz.base.module_1")) + print(loader.load_classes("tests.clazz.packageA")) + print(loader.load_function("./tests/func/base/func1.py")) + print(loader.load_functions("tests/func/packageA")) + print("--- end test -------------------------") + + +if __name__ == "__main__": + main() diff --git a/tests/packageA/module_a1.py b/tests/packageA/module_a1.py deleted file mode 100644 index 2ac6cd5..0000000 --- a/tests/packageA/module_a1.py +++ /dev/null @@ -1,5 +0,0 @@ -from tests.test_module import TestModule - - -class ModuleA1(TestModule): - pass \ No newline at end of file diff --git a/tests/packageA/module_a3.py b/tests/packageA/module_a3.py deleted file mode 100644 index f1d5fd4..0000000 --- a/tests/packageA/module_a3.py +++ /dev/null @@ -1,5 +0,0 @@ -from tests.test_module import TestModule - - -class ModuleA3(TestModule): - pass \ No newline at end of file diff --git a/tests/packageA/packageB/module_b2.py b/tests/packageA/packageB/module_b2.py deleted file mode 100644 index 6871d0c..0000000 --- a/tests/packageA/packageB/module_b2.py +++ /dev/null @@ -1,5 +0,0 @@ -from tests.test_module import TestModule - - -class ModuleB2(TestModule): - pass \ No newline at end of file diff --git a/tests/packageA/packageB/module_b3.py b/tests/packageA/packageB/module_b3.py deleted file mode 100644 index d2ad34d..0000000 --- a/tests/packageA/packageB/module_b3.py +++ /dev/null @@ -1,5 +0,0 @@ -from tests.test_module import TestModule - - -class ModuleB3(TestModule): - pass \ No newline at end of file