[Python-modules-commits] [python-public] 01/03: import python-public_0.4.orig.tar.gz

Barry Warsaw barry at moszumanska.debian.org
Tue Dec 13 00:42:05 UTC 2016


This is an automated email from the git hooks/post-receive script.

barry pushed a commit to branch master
in repository python-public.

commit c27f42eeb5c8ab1db0acd0bdab113afe080f52e0
Author: Barry Warsaw <barry at python.org>
Date:   Mon Dec 12 18:06:04 2016 -0500

    import python-public_0.4.orig.tar.gz
---
 LICENSE.txt                            |  13 ++
 MANIFEST.in                            |   8 ++
 NEWS.rst                               |  22 +++
 PKG-INFO                               |  24 ++++
 README.rst                             | 250 +++++++++++++++++++++++++++++++++
 atpublic.egg-info/PKG-INFO             |  24 ++++
 atpublic.egg-info/SOURCES.txt          |  17 +++
 atpublic.egg-info/dependency_links.txt |   1 +
 atpublic.egg-info/top_level.txt        |   1 +
 conf.py                                | 239 +++++++++++++++++++++++++++++++
 coverage.ini                           |  13 ++
 public/__init__.py                     |  30 ++++
 public/public.py                       |  25 ++++
 public/tests/__init__.py               |   0
 public/tests/test_public.py            | 228 ++++++++++++++++++++++++++++++
 setup.cfg                              |   5 +
 setup.py                               |  55 ++++++++
 setup_helpers.py                       | 153 ++++++++++++++++++++
 tox.ini                                |  56 ++++++++
 19 files changed, 1164 insertions(+)

diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..0b2c13c
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,13 @@
+Copyright 2016 Barry Warsaw
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may not
+use this file except in compliance with the License.  You may obtain a copy of
+the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+License for the specific language governing permissions and limitations under
+the License.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..f3b2742
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,8 @@
+include *.py MANIFEST.in
+global-include *.txt *.rst *.ini
+prune build
+prune dist
+prune .tox
+prune .git
+exclude .gitignore
+exclude *.so
diff --git a/NEWS.rst b/NEWS.rst
new file mode 100644
index 0000000..f5f66c7
--- /dev/null
+++ b/NEWS.rst
@@ -0,0 +1,22 @@
+==============
+ @public NEWS
+==============
+
+0.4 (2016-11-28)
+================
+* Add Python 3.6 support.
+* Make building the C extension optional, for environments without a C
+  compiler.
+
+0.3 (2016-05-25)
+================
+* Raise ``ValueError`` when ``__all__`` isn't a list (or subclass) instance.
+
+0.2 (2016-05-22)
+================
+* Documentation updates based on initial feedback.
+* Some minor test suite clean up.
+
+0.1 (2016-05-09)
+================
+* Initial release.
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..44e6efc
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,24 @@
+Metadata-Version: 1.1
+Name: atpublic
+Version: 0.4
+Summary: public -- @public for populating __all__
+Home-page: http://public.readthedocs.io/
+Author: Barry Warsaw
+Author-email: barry at python.org
+License: Apache 2.0
+Description: This is a very simple decorator and function which populates a module's
+        __all__ and optionally the module globals.
+        
+        This provides both a pure-Python implementation and a C implementation.  It is
+        proposed that the C implementation be added to builtins_ for Python 3.6.
+        
+Platform: UNKNOWN
+Classifier: Development Status :: 4 - Beta
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Operating System :: POSIX
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: Utilities
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..9c41aa5
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,250 @@
+=========
+ @public
+=========
+
+This is a very simple decorator and function which populates a module's
+``__all__`` and optionally the module globals.  This provides both a
+pure-Python implementation and an optional C implementation.
+
+
+Background
+==========
+
+``__all__`` is great.  It has both a functional and a documentation purpose.
+
+The functional purpose is that it `directly controls`_ which module names are
+imported by the ``from <module> import *`` statement.  In the absence of an
+``__all__``, when this statement is executed, every name in ``<module>`` that
+does not start with an underscore will be imported.  This often leads to
+importing too many names into the module.  That's a good enough reason not to
+use ``from <module> import *`` with modules that don't have an ``__all__``.
+
+In the presence of an ``__all__``, only the names specified in this list are
+imported by the ``from <module> import *`` statement.  This in essence gives
+the ``<module>`` author a way to explicitly state which names are for public
+consumption.
+
+And that's the second purpose of ``__all__``; it serves as module
+documentation, explicitly naming the public objects it wants to export.  You
+can print a module's ``__all__`` and get an explicit declaration of its public
+API.
+
+
+The problem
+===========
+
+``__all__`` has two problems.
+
+First, it separates the declaration of a name's public export semantics from
+the implementation of that name.  Usually the ``__all__`` is put at the top of
+the module, although this isn't required, and in some cases it's `actively
+prohibited`_.  So when you're looking at the definition of a function or class
+in a module, you have to search for the ``__all__`` definition to know whether
+the function or class is intended for public consumption.
+
+This leads to the second problem, which is that it's too easy for the
+``__all__`` to get `out of sync`_ with the module's contents.  Often a
+function or class is renamed, removed, or added without the ``__all__`` being
+updated.  Then it's difficult to know what the module author's intent was, and
+it can lead to an exception when a string appearing in ``__all__`` doesn't
+match an existing name in the module.
+
+
+The solution
+============
+
+The solution is to provide a way to declare a name's *publicness* right at the
+point of its declaration, and to infer the name to export from that
+definition.  In this way, a module's author never explicitly sets the
+``__all__`` so there's no way for it to get out of sync.
+
+This package, and Python `issue 26632`_, propose just such a solution, in the
+form of a ``public`` builtin that can be used as either a decorator, or a
+callable.
+
+You'll usually use this as a decorator, for example::
+
+    @public
+    def foo():
+        pass
+
+or::
+
+    @public
+    class Bar:
+        pass
+
+If you were to print the ``__all__`` after both of those code snippets, you'd
+see::
+
+    >>> print(__all__)
+    ['foo', 'Bar']
+
+Note that you do not need to initialize ``__all__`` in the module, since
+``public`` will do it for you.  Of course, if your module *already* has an
+``__all__``, it will just append new names to the existing list.
+
+The requirements to use the ``@public`` decorator are simple: the decorated
+thing must have a ``__name__`` attribute.  Since you'll overwhelmingly use it
+to decorate functions and classes, this will always be the case.
+
+There's one other common use case that isn't covered by the ``@public``
+decorator.  Sometimes you want to declare simple constants or instances as
+publicly available.  You can't use the ``@public`` decorator for two reasons:
+constants don't have a ``__name__`` and Python's syntax doesn't allow you to
+decorate such constructs.
+
+To solve this use case, ``public`` is also a callable function accepting
+keyword arguments.  An example makes this obvious::
+
+    public(SEVEN=7)
+    public(a_bar=Bar())
+
+Now if you print the module's ``__all__`` you'll see::
+
+    >>> print(__all__)
+    ['foo', 'Bar', 'SEVEN', 'a_bar']
+
+and as should be obvious, the module contains name bindings for these
+constants::
+
+    >>> print(SEVEN)
+    7
+    >>> print(a_bar)
+    <__main__.Bar object at ...>
+
+**Note:** While you can use ``public()`` with multiple keyword arguments in a
+single call, the order of the resulting ``__all__`` entries is undefined in
+Python versions earlier than 3.6, due to indeterminate dictionary sort order.
+If order matters to you, call ``public()`` multiple times each with a single
+keyword argument.
+
+
+Usage
+=====
+
+To use this, just import it::
+
+    >>> from public import public
+
+This package actually provides both a pure Python implementation and an
+optional C extension module.  By default, the import above provides you with
+the more efficient C implementation, if available.  See the installation
+instructions below for details.
+
+If for some reason you want the pure-Python implementation just do::
+
+    >>> from public import py_public as public
+
+Having to do this import in every module you want to use it can get pretty
+tedious, so what if you could put ``public`` into Python's builtins?  Then it
+would be available in all your code for free::
+
+    >>> from public import install
+    >>> install()
+
+and now you can just use ``@public`` without having to import anything in your
+other modules.
+
+By default, this installs the C implementation but if you wanted to install
+the pure-Python version, just do::
+
+    >>> from public import py_install
+    >>> py_install()
+
+
+Installation
+============
+
+Use the normal ``setup.py install`` or ``pip install`` commands to install
+this library.  By default, the C extension is **not** built, in order to make
+it more portable to environments without a C compiler.  If you want a version
+that's a little more efficient than the pure-Python implementation, set the
+environment variable ``ATPUBLIC_BUILD_EXTENSION=1`` when you build/install the
+module.
+
+
+Caveats
+=======
+
+There are some important usage restrictions you should be aware of:
+
+* Only use ``@public`` on top-level object.  Specifically, don't try to use
+  ``@public`` on a class method name.  While the declaration won't fail, when
+  You will get an exception when you attempt to ``from <module> import *``
+  because the name pulled from ``__all__`` won't be in the module's globals.
+* If you explicitly set ``__all__`` in your module, be sure to set it to a
+  list.  Some style guides require ``__all__`` to be a tuple, but since that's
+  immutable, as soon as ``@public`` tries to append to it, you will get an
+  exception.  Best practice is to not set ``__all__`` explicitly; let
+  ``@public`` do it!
+* If you still want ``__all__`` to be immutable, put the following at the
+  bottom of your module::
+
+    __all__ = tuple(__all__)
+
+
+Alternatives
+============
+
+This isn't a unique approach to ``@public``.  Other_ implementations_ do
+exist.  There are some subtle differences between this package and those
+others.  This package:
+
+* uses keyword arguments to map names which don't have an ``__name__``
+  attribute;
+* can be used to bind names and values into a module's globals;
+* provides both C and Python implementations;
+* can optionally put ``public`` in builtins.
+
+
+Author
+======
+
+``public`` is Copyright (C) 2016 Barry Warsaw
+
+Contact Barry:
+
+* barry at python.org
+* @pumpichank on Twitter
+* @warsaw on GitHub and GitLab
+
+Licensed under the terms of the Apache License 2.0.  See LICENSE.txt for
+details.
+
+
+Project details
+===============
+
+* Project home: https://gitlab.com/warsaw/public
+* Report bugs at: https://gitlab.com/warsaw/public/issues
+* Fork the code: https://gitlab.com/warsaw/public.git
+* Documentation: http://public.readthedocs.io/en/latest/
+* PyPI: https://pypi.python.org/pypi/atpublic
+
+
+NEWS
+====
+
+.. toctree::
+   :maxdepth: 2
+
+   NEWS
+
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
+
+
+.. _`issue 26632`: http://bugs.python.org/issue26632
+.. _builtins: https://docs.python.org/3/library/builtins.html
+.. _`directly controls`: https://docs.python.org/3/tutorial/modules.html#importing-from-a-package
+.. _`actively prohibited`: http://pep8.readthedocs.io/en/latest/intro.html?highlight=e402#error-codes
+.. _`out of sync`: http://bugs.python.org/issue23883
+.. _Other: https://pypi.python.org/pypi/public
+.. _implementations: http://bugs.python.org/issue22247#msg225637
diff --git a/atpublic.egg-info/PKG-INFO b/atpublic.egg-info/PKG-INFO
new file mode 100644
index 0000000..44e6efc
--- /dev/null
+++ b/atpublic.egg-info/PKG-INFO
@@ -0,0 +1,24 @@
+Metadata-Version: 1.1
+Name: atpublic
+Version: 0.4
+Summary: public -- @public for populating __all__
+Home-page: http://public.readthedocs.io/
+Author: Barry Warsaw
+Author-email: barry at python.org
+License: Apache 2.0
+Description: This is a very simple decorator and function which populates a module's
+        __all__ and optionally the module globals.
+        
+        This provides both a pure-Python implementation and a C implementation.  It is
+        proposed that the C implementation be added to builtins_ for Python 3.6.
+        
+Platform: UNKNOWN
+Classifier: Development Status :: 4 - Beta
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: Apache Software License
+Classifier: Operating System :: POSIX
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Programming Language :: Python :: 3
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: Utilities
diff --git a/atpublic.egg-info/SOURCES.txt b/atpublic.egg-info/SOURCES.txt
new file mode 100644
index 0000000..e3ffc12
--- /dev/null
+++ b/atpublic.egg-info/SOURCES.txt
@@ -0,0 +1,17 @@
+LICENSE.txt
+MANIFEST.in
+NEWS.rst
+README.rst
+conf.py
+coverage.ini
+setup.py
+setup_helpers.py
+tox.ini
+atpublic.egg-info/PKG-INFO
+atpublic.egg-info/SOURCES.txt
+atpublic.egg-info/dependency_links.txt
+atpublic.egg-info/top_level.txt
+public/__init__.py
+public/public.py
+public/tests/__init__.py
+public/tests/test_public.py
\ No newline at end of file
diff --git a/atpublic.egg-info/dependency_links.txt b/atpublic.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/atpublic.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/atpublic.egg-info/top_level.txt b/atpublic.egg-info/top_level.txt
new file mode 100644
index 0000000..a48cf0d
--- /dev/null
+++ b/atpublic.egg-info/top_level.txt
@@ -0,0 +1 @@
+public
diff --git a/conf.py b/conf.py
new file mode 100644
index 0000000..4cf68da
--- /dev/null
+++ b/conf.py
@@ -0,0 +1,239 @@
+# -*- coding: utf-8 -*-
+#
+# This file is execfile()d with the current directory set to its containing
+# dir.
+#
+# Note that not all possible configuration values are present in this
+# autogenerated file.
+#
+# All configuration values have a default; values that are commented out
+# serve to show the default.
+
+import sys, os
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#sys.path.insert(0, os.path.abspath('.'))
+
+# -- General configuration -----------------------------------------------------
+
+# If your documentation needs a minimal Sphinx version, state it here.
+#needs_sphinx = '1.0'
+
+# Add any Sphinx extension module names here, as strings. They can be extensions
+# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
+extensions = ['sphinx.ext.autodoc',
+              'sphinx.ext.viewcode',
+              'sphinx.ext.graphviz']
+
+# Add any paths that contain templates here, relative to this directory.
+# templates_path = ['_templates']
+
+# The suffix of source filenames.
+source_suffix = '.rst'
+
+# The encoding of source files.
+#source_encoding = 'utf-8-sig'
+
+# The master toctree document.
+master_doc = 'README'
+
+# General information about the project.
+project = u'public'
+copyright = u'2016 by Barry Warsaw'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+from public import __version__ as version
+# The full version, including alpha/beta/rc tags.
+release = version
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+#language = None
+
+# There are two options for replacing |today|: either, you set today to some
+# non-false value, then it is used:
+#today = ''
+# Else, today_fmt is used as the format for a strftime call.
+#today_fmt = '%B %d, %Y'
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+exclude_patterns = ['_build', 'eggs', '.tox']
+
+# The reST default role (used for this markup: `text`) to use for all documents.
+#default_role = None
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+#add_function_parentheses = True
+
+# If true, the current module name will be prepended to all description
+# unit titles (such as .. function::).
+#add_module_names = True
+
+# If true, sectionauthor and moduleauthor directives will be shown in the
+# output. They are ignored by default.
+#show_authors = False
+
+# The name of the Pygments (syntax highlighting) style to use.
+pygments_style = 'sphinx'
+
+# A list of ignored prefixes for module index sorting.
+#modindex_common_prefix = []
+
+
+# -- Options for HTML output ---------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+html_theme = 'default'
+
+# Theme options are theme-specific and customize the look and feel of a theme
+# further.  For a list of options available for each theme, see the
+# documentation.
+#html_theme_options = {}
+
+# Add any paths that contain custom themes here, relative to this directory.
+#html_theme_path = []
+
+# The name for this set of Sphinx documents.  If None, it defaults to
+# "<project> v<release> documentation".
+#html_title = None
+
+# A shorter title for the navigation bar.  Default is the same as html_title.
+#html_short_title = None
+
+# The name of an image file (relative to this directory) to place at the top
+# of the sidebar.
+#html_logo = None
+
+# The name of an image file (within the static path) to use as favicon of the
+# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#html_favicon = None
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+# html_static_path = ['_static']
+
+# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
+# using the given strftime format.
+#html_last_updated_fmt = '%b %d, %Y'
+
+# If true, SmartyPants will be used to convert quotes and dashes to
+# typographically correct entities.
+#html_use_smartypants = True
+
+# Custom sidebar templates, maps document names to template names.
+#html_sidebars = {}
+
+# Additional templates that should be rendered to pages, maps page names to
+# template names.
+#html_additional_pages = {}
+
+# If false, no module index is generated.
+#html_domain_indices = True
+
+# If false, no index is generated.
+#html_use_index = True
+
+# If true, the index is split into individual pages for each letter.
+#html_split_index = False
+
+# If true, links to the reST sources are added to the pages.
+#html_show_sourcelink = True
+
+# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#html_show_sphinx = True
+
+# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#html_show_copyright = True
+
+# If true, an OpenSearch description file will be output, and all pages will
+# contain a <link> tag referring to it.  The value of this option must be the
+# base URL from which the finished HTML is served.
+#html_use_opensearch = ''
+
+# This is the file name suffix for HTML files (e.g. ".xhtml").
+#html_file_suffix = None
+
+# Output file base name for HTML help builder.
+htmlhelp_basename = 'public'
+
+
+# -- Options for LaTeX output --------------------------------------------------
+
+# The paper size ('letter' or 'a4').
+#latex_paper_size = 'letter'
+
+# The font size ('10pt', '11pt' or '12pt').
+#latex_font_size = '10pt'
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title, author, documentclass [howto/manual]).
+latex_documents = [
+  ('index', 'public.tex', u'public Documentation',
+   u'Barry Warsaw', 'manual'),
+]
+
+# The name of an image file (relative to this directory) to place at the top of
+# the title page.
+#latex_logo = None
+
+# For "manual" documents, if this is true, then toplevel headings are parts,
+# not chapters.
+#latex_use_parts = False
+
+# If true, show page references after internal links.
+#latex_show_pagerefs = False
+
+# If true, show URL addresses after external links.
+#latex_show_urls = False
+
+# Additional stuff for the LaTeX preamble.
+#latex_preamble = ''
+
+# Documents to append as an appendix to all manuals.
+#latex_appendices = []
+
+# If false, no module index is generated.
+#latex_domain_indices = True
+
+
+# -- Options for manual page output --------------------------------------------
+
+# One entry per manual page. List of tuples
+# (source start file, name, description, authors, manual section).
+man_pages = [
+    ('index', 'public', u'public Documentation',
+     [u'Barry Warsaw'], 1)
+]
+
+
+def index_html():
+    import errno
+    cwd = os.getcwd()
+    try:
+        try:
+            os.makedirs('build/sphinx/html')
+        except OSError as error:
+            if error.errno != errno.EEXIST:
+                raise
+        os.chdir('build/sphinx/html')
+        try:
+            os.symlink('README.html', 'index.html')
+            print('index.html -> README.html')
+        except OSError as error:
+            if error.errno != errno.EEXIST:
+                raise
+    finally:
+        os.chdir(cwd)
+
+import atexit
+atexit.register(index_html)
diff --git a/coverage.ini b/coverage.ini
new file mode 100644
index 0000000..46049cd
--- /dev/null
+++ b/coverage.ini
@@ -0,0 +1,13 @@
+[run]
+branch = true
+parallel = true
+omit =
+     setup*
+    .tox/*/lib/python*/site-packages/*
+    public/tests/*
+    /tmp/*
+
+[paths]
+source =
+    public
+    .tox/*/lib/python*/site-packages/public
diff --git a/public/__init__.py b/public/__init__.py
new file mode 100644
index 0000000..e9c3128
--- /dev/null
+++ b/public/__init__.py
@@ -0,0 +1,30 @@
+# Copyright (C) 2016 Barry Warsaw
+#
+# This project is licensed under the terms of the Apache 2.0 License.  See
+# LICENSE.txt for details.
+
+"""@public -- populate __all__"""
+
+from public.public import public as py_public
+try:
+    from _public import public as c_public
+except ImportError:                                 # pragma: nocover
+    # This library was built without the extension module.
+    c_public = None
+
+
+__version__ = '0.4'
+
+
+if c_public is None:                                # pragma: nocover
+    py_public(public=py_public)
+    py_public(py_public=py_public)
+else:                                               # pragma: nocover
+    c_public(public=c_public)
+    c_public(py_public=py_public)
+
+
+def install():
+    """Install @public into builtins."""
+    import builtins
+    builtins.public = c_public or py_public
diff --git a/public/public.py b/public/public.py
new file mode 100644
index 0000000..27ea415
--- /dev/null
+++ b/public/public.py
@@ -0,0 +1,25 @@
+# Copyright (C) 2016 Barry Warsaw
+#
+# This project is licensed under the terms of the Apache 2.0 License.  See
+# LICENSE.txt for details.
+
+"""Pure-Python implementation."""
+
+import sys
+
+
+# http://bugs.python.org/issue26632
+def public(thing=None, **kws):
+    mdict = (sys._getframe(1).f_globals
+             if thing is None
+             else sys.modules[thing.__module__].__dict__)
+    dunder_all = mdict.setdefault('__all__', [])
+    if not isinstance(dunder_all, list):
+        raise ValueError(
+            '__all__ must be a list not: {}'.format(type(dunder_all)))
+    if thing is not None:
+        dunder_all.append(thing.__name__)
+    for key, value in kws.items():
+        dunder_all.append(key)
+        mdict[key] = value
+    return thing
diff --git a/public/tests/__init__.py b/public/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/public/tests/test_public.py b/public/tests/test_public.py
new file mode 100644
index 0000000..d69ba86
--- /dev/null
+++ b/public/tests/test_public.py
@@ -0,0 +1,228 @@
+# Copyright (C) 2016 Barry Warsaw
+#
+# This project is licensed under the terms of the Apache 2.0 License.  See
+# LICENSE.txt for details.
+
+import os
+import sys
+import builtins
+import unittest
+
+try:
+    from _public import public as c_public
+except ImportError:
+    # This library was built without the extension module.
+    c_public = None
+from contextlib import ExitStack, contextmanager
+from importlib import import_module
+from public import install
+from public.public import public as py_public
+from tempfile import TemporaryDirectory
+
+
+ at contextmanager
+def syspath(directory):
+    try:
+        sys.path.insert(0, directory)
+        yield
+    finally:
+        assert sys.path[0] == directory
+        del sys.path[0]
+
+
+ at contextmanager
+def sysmodules():
+    modules = sys.modules.copy()
+    try:
+        yield
+    finally:
+        sys.modules = modules
+
+
+class TestPublic(unittest.TestCase):
+    import_line = 'from public import public'
+
+    def setUp(self):
+        self.resources = ExitStack()
+        self.addCleanup(self.resources.close)
+        self.tmpdir = self.resources.enter_context(TemporaryDirectory())
+        self.resources.enter_context(syspath(self.tmpdir))
+        self.resources.enter_context(sysmodules())
+        self.modpath = os.path.join(self.tmpdir, 'example.py')
+
+    def test_atpublic_function(self):
+        with open(self.modpath, 'w', encoding='utf-8') as fp:
+            print("""\
+{}
+
+ at public
+def a_function():
+    pass
+""".format(self.import_line), file=fp)
+        module = import_module('example')
+        self.assertEqual(module.__all__, ['a_function'])
+
+    def test_atpublic_function_runnable(self):
+        with open(self.modpath, 'w', encoding='utf-8') as fp:
+            print("""\
+{}
+
+ at public
+def a_function():
+    return 1
+""".format(self.import_line), file=fp)
+        module = import_module('example')
+        self.assertEqual(module.a_function(), 1)
+
+    def test_atpublic_class(self):
+        with open(self.modpath, 'w', encoding='utf-8') as fp:
+            print("""\
+{}
+
+ at public
+class AClass:
+    pass
+""".format(self.import_line), file=fp)
+        module = import_module('example')
+        self.assertEqual(module.__all__, ['AClass'])
+
+    def test_atpublic_class_runnable(self):
+        with open(self.modpath, 'w', encoding='utf-8') as fp:
+            print("""\
+{}
+
+ at public
+class AClass:
+    pass
+""".format(self.import_line), file=fp)
+        module = import_module('example')
+        self.assertIsInstance(module.AClass(), module.AClass)
+
+    def test_atpublic_two_things(self):
+        with open(self.modpath, 'w', encoding='utf-8') as fp:
+            print("""\
+{}
+
+ at public
+def foo():
+    pass
+
+ at public
+class AClass:
+    pass
+""".format(self.import_line), file=fp)
+        module = import_module('example')
+        self.assertEqual(module.__all__, ['foo', 'AClass'])
+
+    def test_atpublic_append_to_all(self):
+        with open(self.modpath, 'w', encoding='utf-8') as fp:
+            print("""\
+__all__ = ['a', 'b']
+
+a = 1
+b = 2
+
+{}
+
+ at public
+def foo():
+    pass
+
+ at public
+class AClass:
+    pass
+""".format(self.import_line), file=fp)
+        module = import_module('example')
+        self.assertEqual(module.__all__, ['a', 'b', 'foo', 'AClass'])
+
+    def test_atpublic_keywords(self):
+        with open(self.modpath, 'w', encoding='utf-8') as fp:
+            print("""\
+{}
+
+public(a=1, b=2)
+""".format(self.import_line), file=fp)
+        module = import_module('example')
+        self.assertEqual(sorted(module.__all__), ['a', 'b'])
+
+    def test_atpublic_keywords_multicall(self):
+        with open(self.modpath, 'w', encoding='utf-8') as fp:
+            print("""\
+{}
+
+public(b=1)
+public(a=2)
+""".format(self.import_line), file=fp)
+        module = import_module('example')
+        self.assertEqual(module.__all__, ['b', 'a'])
+
+    def test_atpublic_keywords_global_bindings(self):
+        with open(self.modpath, 'w', encoding='utf-8') as fp:
+            print("""\
+{}
+
+public(a=1, b=2)
+""".format(self.import_line), file=fp)
+        module = import_module('example')
+        self.assertEqual(module.a, 1)
+        self.assertEqual(module.b, 2)
+
+    def test_atpublic_mixnmatch(self):
+        with open(self.modpath, 'w', encoding='utf-8') as fp:
+            print("""\
+__all__ = ['a', 'b']
+
+a = 1
+b = 2
+
+{}
+
+ at public
+def foo():
+    pass
+
+ at public
+class AClass:
+    pass
+
+public(c=3)
+""".format(self.import_line), file=fp)
+        module = import_module('example')
+        self.assertEqual(module.__all__, ['a', 'b', 'foo', 'AClass', 'c'])
+
+    def test_all_is_a_tuple(self):
+        with open(self.modpath, 'w', encoding='utf-8') as fp:
+            print("""\
+__all__ = ('foo',)
+
+{}
+
+def foo():
+    pass
+
+ at public
+def bar():
+    pass
+""".format(self.import_line), file=fp)
+        self.assertRaises(ValueError, import_module, 'example')
+
+
+class TestPyPublic(TestPublic):
+    import_line = 'from public import py_public as public'
+
+
+class TestInstall(unittest.TestCase):
+    @unittest.skipIf(c_public is None, 'Built without the extension module')
+    def test_install_c_public(self):
+        self.assertFalse(hasattr(builtins, 'public'))
+        self.addCleanup(delattr, builtins, 'public')
... 303 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-public.git



More information about the Python-modules-commits mailing list