Commit 1aac63f4 authored by Daniel Stender's avatar Daniel Stender

Import Upstream version 1.4

parents
# These lines are suggested according to the svn:ignore property
# Feel free to enable them by uncommenting them
syntax:glob
*.pyc
*.pyo
*.swp
*.html
*.class
.tox
build
dist
apipkg.egg-info
.cache/
.eggs/
.env/
4e25c6a58cb618bdcd061289dd66b3ceb11a495a 1.0.0b5
976aa691cfaeeb9793057d09ae9f9e6fd162be69 1.0b5
6cb3976c8d8aac3d332ed8f507cfdad34b4279a1 1.0b6
d83f3dccbd847d054cacaaca24f4a93e04f57219 1.0
99c97a16360ab551455e00861c3e92269d3fa3f7 1.2
82fd1ef6991cfb25b903944684291bf146c86f95 1.3
1.4
-----------------
- revert the automated version gathering
1.3
----------------------------------------
- fix issue2 - adapt tests on Jython
- handle jython __pkgpath__ missabstraction when running python from jar files
- alias modules pointing to unimportable modules will return None for
all their attributes instead of raising ImportError. This addresses
python3.4 where any call to getframeinfo() can choke on sys.modules
contents if pytest is not installed (because py.test.* imports it).
- introduce apipkg.distribution_version(name) as helper to
obtain the current version number of a package from install metadata
its used by default with the package name
- add an eagerloading option and eagerload automatically
if bpython is used (workaround for their monkeypatching)
1.2
----------------------------------------
- Allow to import from Aliasmodules (thanks Ralf Schmitt)
- avoid loading __doc__ early, so ApiModule is now fully lazy
1.1
----------------------------------------
- copy __doc__ and introduce a new argument to initpkg()
(thanks Ralf Schmitt)
- don't use a "0" default for __version__
1.0
----------------------------------------
- make apipkg memorize the absolute path where a package starts
importing so that subsequent chdir + imports won't break.
- allow to alias modules
- allow to use dotted names in alias specifications (thanks Ralf
Schmitt).
1.0.0b6
----------------------------------------
- fix recursive import issue resulting in a superflous KeyError
- default to __version__ '0' and not set __loader__ or __path__ at all if it
doesn't exist on the underlying init module
1.0.0b5
----------------------------------------
- fixed MANIFEST.in
- also transfer __loader__ attribute (thanks Ralf Schmitt)
- compat fix for BPython
1.0.0b3 (compared to 1.0.0b2)
------------------------------------
- added special __onfirstaccess__ attribute whose value will
be called on the first attribute access of an apimodule.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
include tox.ini
include setup.cfg
include CHANGELOG
include test_apipkg.py
Metadata-Version: 1.1
Name: apipkg
Version: 1.4
Summary: apipkg: namespace control and lazy-import mechanism
Home-page: http://bitbucket.org/hpk42/apipkg
Author: holger krekel
Author-email: holger at merlinux.eu
License: MIT License
Description: Welcome to apipkg!
------------------------
With apipkg you can control the exported namespace of a
python package and greatly reduce the number of imports for your users.
It is a `small pure python module`_ that works on virtually all Python
versions, including CPython2.3 to Python3.1, Jython and PyPy. It co-operates
well with Python's ``help()`` system, custom importers (PEP302) and common
command line completion tools.
Usage is very simple: you can require 'apipkg' as a dependency or you
can copy paste the <200 Lines of code into your project.
Tutorial example
-------------------
Here is a simple ``mypkg`` package that specifies one namespace
and exports two objects imported from different modules::
# mypkg/__init__.py
import apipkg
apipkg.initpkg(__name__, {
'path': {
'Class1': "_mypkg.somemodule:Class1",
'clsattr': "_mypkg.othermodule:Class2.attr",
}
}
The package is initialized with a dictionary as namespace.
You need to create a ``_mypkg`` package with a ``somemodule.py``
and ``othermodule.py`` containing the respective classes.
The ``_mypkg`` is not special - it's a completely
regular python package.
Namespace dictionaries contain ``name: value`` mappings
where the value may be another namespace dictionary or
a string specifying an import location. On accessing
an namespace attribute an import will be performed::
>>> import mypkg
>>> mypkg.path
<ApiModule 'mypkg.path'>
>>> mypkg.path.Class1 # '_mypkg.somemodule' gets imported now
<class _mypkg.somemodule.Class1 at 0xb7d428fc>
>>> mypkg.path.clsattr # '_mypkg.othermodule' gets imported now
4 # the value of _mypkg.othermodule.Class2.attr
The ``mypkg.path`` namespace and its two entries are
loaded when they are accessed. This means:
* lazy loading - only what is actually needed is ever loaded
* only the root "mypkg" ever needs to be imported to get
access to the complete functionality.
* the underlying modules are also accessible, for example::
from mypkg.sub import Class1
Including apipkg in your package
--------------------------------------
If you don't want to add an ``apipkg`` dependency to your package you
can copy the `apipkg.py`_ file somewhere to your own package,
for example ``_mypkg/apipkg.py`` in the above example. You
then import the ``initpkg`` function from that new place and
are good to go.
.. _`small pure python module`:
.. _`apipkg.py`: http://bitbucket.org/hpk42/apipkg/src/tip/apipkg.py
Feedback?
-----------------------
If you have questions you are welcome to
* join the #pylib channel on irc.freenode.net
* subscribe to the http://codespeak.net/mailman/listinfo/py-dev list.
* create an issue on http://bitbucket.org/hpk42/apipkg/issues
have fun,
holger krekel
Platform: unix
Platform: linux
Platform: osx
Platform: cygwin
Platform: win32
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Topic :: Software Development :: Libraries
Classifier: Programming Language :: Python
Welcome to apipkg!
------------------------
With apipkg you can control the exported namespace of a
python package and greatly reduce the number of imports for your users.
It is a `small pure python module`_ that works on virtually all Python
versions, including CPython2.3 to Python3.1, Jython and PyPy. It co-operates
well with Python's ``help()`` system, custom importers (PEP302) and common
command line completion tools.
Usage is very simple: you can require 'apipkg' as a dependency or you
can copy paste the <200 Lines of code into your project.
Tutorial example
-------------------
Here is a simple ``mypkg`` package that specifies one namespace
and exports two objects imported from different modules::
# mypkg/__init__.py
import apipkg
apipkg.initpkg(__name__, {
'path': {
'Class1': "_mypkg.somemodule:Class1",
'clsattr': "_mypkg.othermodule:Class2.attr",
}
}
The package is initialized with a dictionary as namespace.
You need to create a ``_mypkg`` package with a ``somemodule.py``
and ``othermodule.py`` containing the respective classes.
The ``_mypkg`` is not special - it's a completely
regular python package.
Namespace dictionaries contain ``name: value`` mappings
where the value may be another namespace dictionary or
a string specifying an import location. On accessing
an namespace attribute an import will be performed::
>>> import mypkg
>>> mypkg.path
<ApiModule 'mypkg.path'>
>>> mypkg.path.Class1 # '_mypkg.somemodule' gets imported now
<class _mypkg.somemodule.Class1 at 0xb7d428fc>
>>> mypkg.path.clsattr # '_mypkg.othermodule' gets imported now
4 # the value of _mypkg.othermodule.Class2.attr
The ``mypkg.path`` namespace and its two entries are
loaded when they are accessed. This means:
* lazy loading - only what is actually needed is ever loaded
* only the root "mypkg" ever needs to be imported to get
access to the complete functionality.
* the underlying modules are also accessible, for example::
from mypkg.sub import Class1
Including apipkg in your package
--------------------------------------
If you don't want to add an ``apipkg`` dependency to your package you
can copy the `apipkg.py`_ file somewhere to your own package,
for example ``_mypkg/apipkg.py`` in the above example. You
then import the ``initpkg`` function from that new place and
are good to go.
.. _`small pure python module`:
.. _`apipkg.py`: http://bitbucket.org/hpk42/apipkg/src/tip/apipkg.py
Feedback?
-----------------------
If you have questions you are welcome to
* join the #pylib channel on irc.freenode.net
* subscribe to the http://codespeak.net/mailman/listinfo/py-dev list.
* create an issue on http://bitbucket.org/hpk42/apipkg/issues
have fun,
holger krekel
Metadata-Version: 1.1
Name: apipkg
Version: 1.4
Summary: apipkg: namespace control and lazy-import mechanism
Home-page: http://bitbucket.org/hpk42/apipkg
Author: holger krekel
Author-email: holger at merlinux.eu
License: MIT License
Description: Welcome to apipkg!
------------------------
With apipkg you can control the exported namespace of a
python package and greatly reduce the number of imports for your users.
It is a `small pure python module`_ that works on virtually all Python
versions, including CPython2.3 to Python3.1, Jython and PyPy. It co-operates
well with Python's ``help()`` system, custom importers (PEP302) and common
command line completion tools.
Usage is very simple: you can require 'apipkg' as a dependency or you
can copy paste the <200 Lines of code into your project.
Tutorial example
-------------------
Here is a simple ``mypkg`` package that specifies one namespace
and exports two objects imported from different modules::
# mypkg/__init__.py
import apipkg
apipkg.initpkg(__name__, {
'path': {
'Class1': "_mypkg.somemodule:Class1",
'clsattr': "_mypkg.othermodule:Class2.attr",
}
}
The package is initialized with a dictionary as namespace.
You need to create a ``_mypkg`` package with a ``somemodule.py``
and ``othermodule.py`` containing the respective classes.
The ``_mypkg`` is not special - it's a completely
regular python package.
Namespace dictionaries contain ``name: value`` mappings
where the value may be another namespace dictionary or
a string specifying an import location. On accessing
an namespace attribute an import will be performed::
>>> import mypkg
>>> mypkg.path
<ApiModule 'mypkg.path'>
>>> mypkg.path.Class1 # '_mypkg.somemodule' gets imported now
<class _mypkg.somemodule.Class1 at 0xb7d428fc>
>>> mypkg.path.clsattr # '_mypkg.othermodule' gets imported now
4 # the value of _mypkg.othermodule.Class2.attr
The ``mypkg.path`` namespace and its two entries are
loaded when they are accessed. This means:
* lazy loading - only what is actually needed is ever loaded
* only the root "mypkg" ever needs to be imported to get
access to the complete functionality.
* the underlying modules are also accessible, for example::
from mypkg.sub import Class1
Including apipkg in your package
--------------------------------------
If you don't want to add an ``apipkg`` dependency to your package you
can copy the `apipkg.py`_ file somewhere to your own package,
for example ``_mypkg/apipkg.py`` in the above example. You
then import the ``initpkg`` function from that new place and
are good to go.
.. _`small pure python module`:
.. _`apipkg.py`: http://bitbucket.org/hpk42/apipkg/src/tip/apipkg.py
Feedback?
-----------------------
If you have questions you are welcome to
* join the #pylib channel on irc.freenode.net
* subscribe to the http://codespeak.net/mailman/listinfo/py-dev list.
* create an issue on http://bitbucket.org/hpk42/apipkg/issues
have fun,
holger krekel
Platform: unix
Platform: linux
Platform: osx
Platform: cygwin
Platform: win32
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Topic :: Software Development :: Libraries
Classifier: Programming Language :: Python
.hgignore
.hgtags
CHANGELOG
LICENSE
MANIFEST.in
README.txt
apipkg.py
setup.cfg
setup.py
test_apipkg.py
tox.ini
apipkg.egg-info/PKG-INFO
apipkg.egg-info/SOURCES.txt
apipkg.egg-info/dependency_links.txt
apipkg.egg-info/top_level.txt
example/_mypkg/__init__.py
example/_mypkg/othermodule.py
example/_mypkg/somemodule.py
example/mypkg/__init__.py
example/mypkg/othermodule.py
example/mypkg/somemodule.py
\ No newline at end of file
"""
apipkg: control the exported namespace of a python package.
see http://pypi.python.org/pypi/apipkg
(c) holger krekel, 2009 - MIT license
"""
import os
import sys
from types import ModuleType
__version__ = '1.4'
def _py_abspath(path):
"""
special version of abspath
that will leave paths from jython jars alone
"""
if path.startswith('__pyclasspath__'):
return path
else:
return os.path.abspath(path)
def distribution_version(name):
"""try to get the version of the named distribution,
returs None on failure"""
from pkg_resources import get_distribution, DistributionNotFound
try:
dist = get_distribution(name)
except DistributionNotFound:
pass
else:
return dist.version
def initpkg(pkgname, exportdefs, attr=dict(), eager=False):
""" initialize given package from the export definitions. """
oldmod = sys.modules.get(pkgname)
d = {}
f = getattr(oldmod, '__file__', None)
if f:
f = _py_abspath(f)
d['__file__'] = f
if hasattr(oldmod, '__version__'):
d['__version__'] = oldmod.__version__
if hasattr(oldmod, '__loader__'):
d['__loader__'] = oldmod.__loader__
if hasattr(oldmod, '__path__'):
d['__path__'] = [_py_abspath(p) for p in oldmod.__path__]
if '__doc__' not in exportdefs and getattr(oldmod, '__doc__', None):
d['__doc__'] = oldmod.__doc__
d.update(attr)
if hasattr(oldmod, "__dict__"):
oldmod.__dict__.update(d)
mod = ApiModule(pkgname, exportdefs, implprefix=pkgname, attr=d)
sys.modules[pkgname] = mod
# eagerload in bypthon to avoid their monkeypatching breaking packages
if 'bpython' in sys.modules or eager:
for module in sys.modules.values():
if isinstance(module, ApiModule):
module.__dict__
def importobj(modpath, attrname):
module = __import__(modpath, None, None, ['__doc__'])
if not attrname:
return module
retval = module
names = attrname.split(".")
for x in names:
retval = getattr(retval, x)
return retval
class ApiModule(ModuleType):
def __docget(self):
try:
return self.__doc
except AttributeError:
if '__doc__' in self.__map__:
return self.__makeattr('__doc__')
def __docset(self, value):
self.__doc = value
__doc__ = property(__docget, __docset)
def __init__(self, name, importspec, implprefix=None, attr=None):
self.__name__ = name
self.__all__ = [x for x in importspec if x != '__onfirstaccess__']
self.__map__ = {}
self.__implprefix__ = implprefix or name
if attr:
for name, val in attr.items():
# print "setting", self.__name__, name, val
setattr(self, name, val)
for name, importspec in importspec.items():
if isinstance(importspec, dict):
subname = '%s.%s' % (self.__name__, name)
apimod = ApiModule(subname, importspec, implprefix)
sys.modules[subname] = apimod
setattr(self, name, apimod)
else:
parts = importspec.split(':')
modpath = parts.pop(0)
attrname = parts and parts[0] or ""
if modpath[0] == '.':
modpath = implprefix + modpath
if not attrname:
subname = '%s.%s' % (self.__name__, name)
apimod = AliasModule(subname, modpath)
sys.modules[subname] = apimod
if '.' not in name:
setattr(self, name, apimod)
else:
self.__map__[name] = (modpath, attrname)
def __repr__(self):
l = []
if hasattr(self, '__version__'):
l.append("version=" + repr(self.__version__))
if hasattr(self, '__file__'):
l.append('from ' + repr(self.__file__))
if l:
return '<ApiModule %r %s>' % (self.__name__, " ".join(l))
return '<ApiModule %r>' % (self.__name__,)
def __makeattr(self, name):
"""lazily compute value for name or raise AttributeError if unknown."""
# print "makeattr", self.__name__, name
target = None
if '__onfirstaccess__' in self.__map__:
target = self.__map__.pop('__onfirstaccess__')
importobj(*target)()
try:
modpath, attrname = self.__map__[name]
except KeyError:
if target is not None and name != '__onfirstaccess__':
# retry, onfirstaccess might have set attrs
return getattr(self, name)
raise AttributeError(name)
else:
result = importobj(modpath, attrname)
setattr(self, name, result)
try:
del self.__map__[name]
except KeyError:
pass # in a recursive-import situation a double-del can happen
return result
__getattr__ = __makeattr
@property
def __dict__(self):
# force all the content of the module
# to be loaded when __dict__ is read
dictdescr = ModuleType.__dict__['__dict__']
dict = dictdescr.__get__(self)
if dict is not None:
hasattr(self, 'some')
for name in self.__all__:
try:
self.__makeattr(name)
except AttributeError:
pass
return dict
def AliasModule(modname, modpath, attrname=None):
mod = []
def getmod():
if not mod:
x = importobj(modpath, None)
if attrname is not None:
x = getattr(x, attrname)
mod.append(x)
return mod[0]
class AliasModule(ModuleType):
def __repr__(self):
x = modpath
if attrname:
x += "." + attrname
return '<AliasModule %r for %r>' % (modname, x)
def __getattribute__(self, name):
try:
return getattr(getmod(), name)
except ImportError:
return None
def __setattr__(self, name, value):
setattr(getmod(), name, value)
def __delattr__(self, name):
delattr(getmod(), name)
return AliasModule(str(modname))
class OtherClass:
pass
from _mypkg.othermodule import OtherClass
class SomeClass:
pass
# mypkg/__init__.py
import apipkg
apipkg.initpkg(__name__, {
'SomeClass': '_mypkg.somemodule:SomeClass',
'sub': {
'OtherClass': '_mypkg.somemodule:OtherClass',
}
})
class OtherClass:
pass
from __mypkg.othermodule import OtherClass
class SomeClass:
pass
[wheel]
universal = true
[egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0
import re
from setuptools import setup
def get_version():
VERSION_RE = re.compile("__version__ = \'(.*)\'", re.M)
with open('apipkg.py') as fp:
return VERSION_RE.search(fp.read()).group(1)
def main():
setup(
name='apipkg',
description='apipkg: namespace control and lazy-import mechanism',
long_description=open('README.txt').read(),
version=get_version(),
url='http://bitbucket.org/hpk42/apipkg',
license='MIT License',
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
author='holger krekel',
author_email='holger at merlinux.eu',
classifiers=[
'Development Status :: 4 - Beta',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: POSIX',
'Operating System :: Microsoft :: Windows',
'Operating System :: MacOS :: MacOS X',
'Topic :: Software Development :: Libraries',
'Programming Language :: Python'],
py_modules=['apipkg'],
)
if __name__ == '__main__':
main()
This diff is collapsed.
[tox]
envlist=py27,py26,py33,py34,jython,flakes
[tox:hudson]
sdistsrc={distshare}/apipkg-*
[testenv]
deps=pytest
commands=py.test []
[testenv:jython]
deps=pytest
commands=py.test-jython []
[testenv:flakes]
deps=flake8
commands=flake8
[flake8]
exclude=.tox/,.env/,dist/,build/,example/
max_complexity=11
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