Commit 3c971d64 authored by Kirill Smelkov's avatar Kirill Smelkov

Top-level in-tree import redirector

So that import xlte.abc resolves to xlte/abc.py

Based on similar top-level import redirector from wendelin.core
See nexedi/wendelin.core@e870781d for
context and links in added code for further details.
parent 3de908b0
...@@ -19,6 +19,47 @@ ...@@ -19,6 +19,47 @@
# See https://www.nexedi.com/licensing for rationale and options. # See https://www.nexedi.com/licensing for rationale and options.
from setuptools import setup, find_packages from setuptools import setup, find_packages
from setuptools.command.build_py import build_py as _build_py
# build_py that
# - prevents in-tree xlte.py & setup.py to be installed
# - synthesizes xlte/__init__.py on install
#
# based on wendelin.core/setup.py:build.py
class build_py(_build_py):
def find_package_modules(self, package, package_dir):
modules = _build_py.find_package_modules(self, package, package_dir)
try:
modules.remove(('xlte', 'xlte', 'xlte.py'))
modules.remove(('xlte', 'setup', 'setup.py'))
except ValueError:
pass # was not there
return modules
def build_packages(self):
_build_py.build_packages(self)
# emit std namespacing mantra to xlte/__init__.py
self.initfile = self.get_module_outfile(self.build_lib, ('xlte',), '__init__')
with open(self.initfile, 'w') as f:
f.write("# this is a namespace package (autogenerated)\n")
f.write("__import__('pkg_resources').declare_namespace(__name__)\n")
def get_outputs(self, include_bytecode=1):
outputs = _build_py.get_outputs(self, include_bytecode)
# add synthesized __init__.py to outputs, so that `pip uninstall`
# works without leaving it
outputs.append(self.initfile)
if include_bytecode:
if self.compile:
outputs.append(self.initfile + 'c')
if self.optimize:
outputs.append(self.initfile + 'o')
return outputs
# read file content # read file content
...@@ -44,6 +85,9 @@ setup( ...@@ -44,6 +85,9 @@ setup(
packages = ['xlte'] + ['xlte.%s' % _ for _ in packages = ['xlte'] + ['xlte.%s' % _ for _ in
find_packages()], find_packages()],
cmdclass = {'build_py': build_py,
},
classifiers = [_.strip() for _ in """\ classifiers = [_.strip() for _ in """\
Development Status :: 2 - Pre-Alpha Development Status :: 2 - Pre-Alpha
Intended Audience :: Developers Intended Audience :: Developers
......
# Copyright (C) 2022 Nexedi SA and Contributors. # XLTE | Top-level in-tree python import redirector
# Based on https://lab.nexedi.com/nexedi/wendelin.core/blob/master/wendelin.py
# Copyright (C) 2014-2022 Nexedi SA and Contributors.
# Kirill Smelkov <kirr@nexedi.com> # Kirill Smelkov <kirr@nexedi.com>
# #
# This program is free software: you can Use, Study, Modify and Redistribute # This program is free software: you can Use, Study, Modify and Redistribute
...@@ -16,3 +18,52 @@ ...@@ -16,3 +18,52 @@
# #
# See COPYING file for full licensing terms. # See COPYING file for full licensing terms.
# See https://www.nexedi.com/licensing for rationale and options. # See https://www.nexedi.com/licensing for rationale and options.
# tell python xlte.* modules hierarchy starts at top-level
#
# This allows e.g. `import xlte.amari`
# to resolve to importing `amari/__init__.py`
#
# and thus avoid putting everything in additional top-level xlte/
# directory in source tree.
#
# see https://www.python.org/doc/essays/packages/ about __path__
# first make sure setuptools will recognize xlte.py as package,
# but do not setup proper __path__ yet.
# ( _handle_ns() checks for __path__ attribute presence and refuses to further
# process "not a package"
#
# https://github.com/pypa/setuptools/blob/9803058d/pkg_resources/__init__.py#L2012 )
__path__ = []
# tell setuptools/pkg_resources 'xlte' is a namespace package
# ( so that xlte installed in development mode does not brake
# 'xlte' namespacing wrt other xlte software )
__import__('pkg_resources').declare_namespace(__name__)
# pkg_resources will append '.../xlte/xlte' to __path__ which is
# not right for in-tree setup and thus needs to be corrected:
# Rewrite '.../xlte/xlte' -> '.../xlte'
from os.path import dirname, realpath, splitext
myfile = realpath(__file__)
mymod = splitext(myfile)[0] # .../xlte.py -> .../xlte
mydir = dirname(myfile) # .../xlte -> ...
i = None # in case vvv loop is empty, so we still can `del i` in the end
for i in range(len(__path__)):
# NOTE realpath(...) for earlier setuptools, where __path__ entry could be
# added as relative
if realpath(__path__[i]) == mymod:
__path__[i] = mydir
del dirname, realpath, splitext, myfile, mymod, mydir, i
# in the end we have:
# __path__ has >= 1 items
# __path__ entry for xlte points to top of working tree
# __name__ registered as namespace package
#
# so the following should work:
# - importing from in-tree files
# - importing from other children of xlte packages
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment