diff --git a/MANIFEST.in b/MANIFEST.in index 1fa3050c..14229e80 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,5 @@ include objects/* -include README.md +include README.rst include LICENSE include VERSION include requirements.txt diff --git a/README.md b/README.md deleted file mode 100644 index 93c63949..00000000 --- a/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# Objects - -Dependency management tool for Python projects. - -[![Latest Version](https://pypip.in/version/Objects/badge.svg)](https://pypi.python.org/pypi/Objects/) -[![Downloads](https://pypip.in/download/Objects/badge.svg)](https://pypi.python.org/pypi/Objects/) -[![Build Status](https://travis-ci.org/rmk135/objects.svg?branch=master)](https://travis-ci.org/rmk135/objects) -[![Coverage Status](https://coveralls.io/repos/rmk135/objects/badge.svg)](https://coveralls.io/r/rmk135/objects) -[![License](https://pypip.in/license/Objects/badge.svg)](https://pypi.python.org/pypi/Objects/) -[![Supported Python versions](https://pypip.in/py_versions/Objects/badge.svg)](https://pypi.python.org/pypi/Objects/) -[![Supported Python implementations](https://pypip.in/implementation/Objects/badge.svg)](https://pypi.python.org/pypi/Objects/) - -## Introduction - -Python ecosystem consists of a big amount of various classes, functions and -objects that could be used for applications development. Each of them has its -own role. - -Modern Python applications are mostly the composition of well-known open -source systems, frameworks, libraries and some turnkey functionality. - -When application goes bigger, its amount of objects and their dependencies -also increased extremely fast and became hard to maintain. - -`Objects` is designed to be developer's friendly tool for managing objects -and their dependencies in formal, pretty way. Main idea of `Objects` is to -keep dependencies under control. - -## Installation - -Objects library is available on [PyPi](https://pypi.python.org/pypi/Objects): - -``` -pip install objects -``` - -## Documentation - -`Objects` documentation is hosted on ReadTheDocs: - -- [Stable version](http://objects.readthedocs.org/en/stable/) -- [Latest version](http://objects.readthedocs.org/en/latest/) diff --git a/README.rst b/README.rst new file mode 100644 index 00000000..da72acdc --- /dev/null +++ b/README.rst @@ -0,0 +1,159 @@ +Objects +======= + +Dependency management tool for Python projects. + ++---------------------------------------+-------------------------------------------------------------------+ +| *PyPi* | .. image:: https://pypip.in/version/Objects/badge.svg | +| | :target: https://pypi.python.org/pypi/Objects/ | +| | :alt: Latest Version | +| | .. image:: https://pypip.in/download/Objects/badge.svg | +| | :target: https://pypi.python.org/pypi/Objects/ | +| | :alt: Downloads | +| | .. image:: https://pypip.in/license/Objects/badge.svg | +| | :target: https://pypi.python.org/pypi/Objects/ | +| | :alt: License | ++---------------------------------------+-------------------------------------------------------------------+ +| *Python versions and implementations* | .. image:: https://pypip.in/py_versions/Objects/badge.svg | +| | :target: https://pypi.python.org/pypi/Objects/ | +| | :alt: Supported Python versions | +| | .. image:: https://pypip.in/implementation/Objects/badge.svg | +| | :target: https://pypi.python.org/pypi/Objects/ | +| | :alt: Supported Python implementations | ++---------------------------------------+-------------------------------------------------------------------+ +| *Builds and test coverage* | .. image:: https://travis-ci.org/rmk135/objects.svg?branch=master | +| | :target: https://travis-ci.org/rmk135/objects | +| | :alt: Build Status | +| | .. image:: https://coveralls.io/repos/rmk135/objects/badge.svg | +| | :target: https://coveralls.io/r/rmk135/objects | +| | :alt: Coverage Status | ++---------------------------------------+-------------------------------------------------------------------+ + +Introduction +------------ + +Python ecosystem consists of a big amount of various classes, functions and +objects that could be used for applications development. Each of them has its +own role. + +Modern Python applications are mostly the composition of well-known open +source systems, frameworks, libraries and some turnkey functionality. + +When application goes bigger, its amount of objects and their dependencies +also increased extremely fast and became hard to maintain. + +**Objects** is designed to be developer's friendly tool for managing objects +and their dependencies in formal, pretty way. Main idea of **Objects** is to +keep dependencies under control. + +Installation +------------ + +**Objects** library is available on PyPi_:: + + pip install objects + +Documentation +------------- + +**Objects** documentation is hosted on ReadTheDocs: + +- `Stable version`_ +- `Latest version`_ + +Examples +-------- + +.. code-block:: python + + """Concept example of `Objects`.""" + + from objects.catalog import AbstractCatalog + + from objects.providers import Singleton + from objects.providers import NewInstance + + from objects.injections import KwArg + from objects.injections import Attribute + from objects.injections import inject + + import sqlite3 + + + class ObjectA(object): + + """Example class ObjectA, that has dependency on database.""" + + def __init__(self, db): + """Initializer.""" + self.db = db + + + class ObjectB(object): + + """Example class ObjectB, that has dependencies on ObjectA and database.""" + + def __init__(self, a, db): + """Initializer.""" + self.a = a + self.db = db + + + class Catalog(AbstractCatalog): + + """Catalog of objects providers.""" + + database = Singleton(sqlite3.Connection, + KwArg('database', ':memory:'), + Attribute('row_factory', sqlite3.Row)) + """:type: (objects.Provider) -> sqlite3.Connection""" + + object_a = NewInstance(ObjectA, + KwArg('db', database)) + """:type: (objects.Provider) -> ObjectA""" + + object_b = NewInstance(ObjectB, + KwArg('a', object_a), + KwArg('db', database)) + """:type: (objects.Provider) -> ObjectB""" + + + # Catalog static provides. + a1, a2 = Catalog.object_a(), Catalog.object_a() + b1, b2 = Catalog.object_b(), Catalog.object_b() + + assert a1 is not a2 + assert b1 is not b2 + assert a1.db is a2.db is b1.db is b2.db is Catalog.database() + + + # Example of inline injections. + @inject(KwArg('a', Catalog.object_a)) + @inject(KwArg('b', Catalog.object_b)) + @inject(KwArg('database', Catalog.database)) + def example(a, b, database): + assert a.db is b.db is database is Catalog.database() + + + example() + +You can get more **Objects** examples in ``/examples`` directory on +GitHub: + + https://github.com/rmk135/objects + + +Feedback +-------- + +Feel free to post questions, bugs, feature requests, proposals etc. on +**Objects** GitHub Issues: + + https://github.com/rmk135/objects/issues + +Your feedback is quite important! + + +.. _PyPi: https://pypi.python.org/pypi/Objects +.. _Stable version: http://objects.readthedocs.org/en/stable/ +.. _Latest version: http://objects.readthedocs.org/en/latest/ diff --git a/docs/Makefile b/docs/Makefile new file mode 100644 index 00000000..2914875a --- /dev/null +++ b/docs/Makefile @@ -0,0 +1,192 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = _build + +# User-friendly check for sphinx-build +ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) +$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) +endif + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . +# the i18n builder cannot share the environment and doctrees with the others +I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " applehelp to make an Apple Help Book" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " texinfo to make Texinfo files" + @echo " info to make Texinfo files and run them through makeinfo" + @echo " gettext to make PO message catalogs" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " xml to make Docutils-native XML files" + @echo " pseudoxml to make pseudoxml-XML files for display purposes" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + @echo " coverage to run coverage check of the documentation (if enabled)" + +clean: + rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/objects.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/objects.qhc" + +applehelp: + $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp + @echo + @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." + @echo "N.B. You won't be able to view it unless you put it in" \ + "~/Library/Documentation/Help or install it in your application" \ + "bundle." + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/objects" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/objects" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +latexpdfja: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through platex and dvipdfmx..." + $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +texinfo: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo + @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." + @echo "Run \`make' in that directory to run these through makeinfo" \ + "(use \`make info' here to do that automatically)." + +info: + $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo + @echo "Running Texinfo files through makeinfo..." + make -C $(BUILDDIR)/texinfo info + @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." + +gettext: + $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale + @echo + @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." + +coverage: + $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage + @echo "Testing of coverage in the sources finished, look at the " \ + "results in $(BUILDDIR)/coverage/python.txt." + +xml: + $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml + @echo + @echo "Build finished. The XML files are in $(BUILDDIR)/xml." + +pseudoxml: + $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml + @echo + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/docs/advanced_usage.rst b/docs/advanced_usage.rst new file mode 100644 index 00000000..7161bb62 --- /dev/null +++ b/docs/advanced_usage.rst @@ -0,0 +1,201 @@ +Advanced usage +============== + +Below you can find some variants of advanced usage of **Objects**. + +@inject decorator +----------------- + +``@inject`` decorator could be used for patching any callable with injection. +Any Python object will be injected *as is*, except **Objects** providers, +that will be called to provide injectable value. + +.. code-block:: python + + """`@inject` decorator example.""" + + from objects.providers import NewInstance + + from objects.injections import KwArg + from objects.injections import inject + + + new_object = NewInstance(object) + + + @inject(KwArg('object_a', new_object)) + @inject(KwArg('some_setting', 1334)) + def example_callback(object_a, some_setting): + """This function has dependencies on object a and b. + + Dependencies are injected using `@inject` decorator. + """ + assert isinstance(object_a, object) + assert some_setting == 1334 + + + example_callback() + example_callback() + +Overriding providers +-------------------- + +Any provider can be overridden by another provider. + +Example: + +.. code-block:: python + + """Provider overriding example.""" + + import sqlite3 + + from objects.providers import Singleton + from objects.providers import NewInstance + + from objects.injections import KwArg + from objects.injections import Attribute + + + class ObjectA(object): + + """ObjectA has dependency on database.""" + + def __init__(self, database): + """Initializer. + + Database dependency need to be injected via init arg.""" + self.database = database + + def get_one(self): + """Select one from database and return it.""" + return self.database.execute('SELECT 1') + + + class ObjectAMock(ObjectA): + + """Mock of ObjectA. + + Has no dependency on database. + """ + + def __init__(self): + """Initializer.""" + + def get_one(self): + """Select one from database and return it. + + Mock makes no database queries and always returns two instead of one. + """ + return 2 + + + # Database and `ObjectA` providers. + database = Singleton(sqlite3.Connection, + KwArg('database', ':memory:'), + KwArg('timeout', 30), + KwArg('detect_types', True), + KwArg('isolation_level', 'EXCLUSIVE'), + Attribute('row_factory', sqlite3.Row)) + + object_a = NewInstance(ObjectA, + KwArg('database', database)) + + + # Overriding `ObjectA` provider with `ObjectAMock` provider. + object_a.override(NewInstance(ObjectAMock)) + + # Creating several `ObjectA` instances. + object_a_1 = object_a() + object_a_2 = object_a() + + # Making some asserts. + assert object_a_1 is not object_a_2 + assert object_a_1.get_one() == object_a_2.get_one() == 2 + +Overriding catalogs +------------------- + +Any catalog can be overridden by another catalog. + +Example: + +.. code-block:: python + + """Catalog overriding example.""" + + import sqlite3 + + from objects.catalog import AbstractCatalog + from objects.catalog import override + + from objects.providers import Singleton + from objects.providers import NewInstance + + from objects.injections import KwArg + from objects.injections import Attribute + + + class ObjectA(object): + + """ObjectA has dependency on database.""" + + def __init__(self, database): + """Initializer. + + Database dependency need to be injected via init arg.""" + self.database = database + + def get_one(self): + """Select one from database and return it.""" + return self.database.execute('SELECT 1') + + + class ObjectAMock(ObjectA): + + """Mock of ObjectA. + + Has no dependency on database. + """ + + def __init__(self): + """Initializer.""" + + def get_one(self): + """Select one from database and return it. + + Mock makes no database queries and always returns two instead of one. + """ + return 2 + + + class Catalog(AbstractCatalog): + + """Catalog of objects providers.""" + + database = Singleton(sqlite3.Connection, + KwArg('database', ':memory:'), + KwArg('timeout', 30), + KwArg('detect_types', True), + KwArg('isolation_level', 'EXCLUSIVE'), + Attribute('row_factory', sqlite3.Row)) + + object_a = NewInstance(ObjectA, + KwArg('database', database)) + + + @override(Catalog) + class SandboxCatalog(Catalog): + + """Sandbox objects catalog with some mocks that overrides Catalog.""" + + object_a = NewInstance(ObjectAMock) + + + # Creating several `ObjectA` instances. + object_a_1 = Catalog.object_a() + object_a_2 = Catalog.object_a() + + # Making some asserts. + assert object_a_1 is not object_a_2 + assert object_a_1.get_one() == object_a_2.get_one() == 2 diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 00000000..40823824 --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,296 @@ +# -*- coding: utf-8 -*- +# +# Objects documentation build configuration file, created by +# sphinx-quickstart on Wed Apr 1 17:36:06 2015. +# +# 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 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 = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Objects' +copyright = u'2015, Roman Mogilatov' +author = u'Roman Mogilatov' + +# 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. +# Getting version. +with open('../VERSION') as version: + version = version.read().strip() + +# 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. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +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'] + +# 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 = [] + +# If true, keep warnings as "system message" paragraphs in the built documents. +#keep_warnings = False + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + + +# -- 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 +# " v 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'] + +# Add any extra paths that contain custom files (such as robots.txt or +# .htaccess) here, relative to this directory. These files are copied +# directly to the root of the documentation. +#html_extra_path = [] + +# 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 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 + +# Language to be used for generating the HTML full-text search index. +# Sphinx supports the following languages: +# 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' +# 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' +#html_search_language = 'en' + +# A dictionary with options for the search language support, empty by default. +# Now only 'ja' uses this config value +#html_search_options = {'type': 'default'} + +# The name of a javascript file (relative to the configuration directory) that +# implements a search results scorer. If empty, the default will be used. +#html_search_scorer = 'scorer.js' + +# Output file base name for HTML help builder. +htmlhelp_basename = 'objectsdoc' + +# on_rtd is whether we are on readthedocs.org, this line of code grabbed from +# docs.readthedocs.org +on_rtd = os.environ.get('READTHEDOCS', None) == 'True' + +if not on_rtd: # only import and set the theme if we're building docs locally + import sphinx_rtd_theme + html_theme = 'sphinx_rtd_theme' + html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] +# otherwise, readthedocs.org uses their theme by default, so no need to +# specify it + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', + +# Latex figure (float) alignment +#'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'Objects.tex', u'Objects Documentation', + u'Roman Mogilatov', '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 + +# 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 = [ + (master_doc, 'Objects', u'Objects Documentation', + [author], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'Objects', u'Objects Documentation', + author, 'Objects', 'Dependency management tool for Python projects.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +# If true, do not generate a @detailmenu in the "Top" node's menu. +#texinfo_no_detailmenu = False diff --git a/docs/entities.rst b/docs/entities.rst new file mode 100644 index 00000000..7bb31c88 --- /dev/null +++ b/docs/entities.rst @@ -0,0 +1,187 @@ +Entities +======== + +Current section describes main **Objects** entities and their interaction. + +Providers +--------- + +Providers are strategies of accessing objects. + +All providers are callable. They describe how particular objects will be +provided. For example: + +.. code-block:: python + + """`NewInstance` and `Singleton` providers example.""" + + from objects.providers import NewInstance + from objects.providers import Singleton + + + # NewInstance provider will create new instance of specified class + # on every call. + new_object = NewInstance(object) + + object_1 = new_object() + object_2 = new_object() + + assert object_1 is not object_2 + + # Singleton provider will create new instance of specified class on first call, + # and return same instance on every next call. + single_object = Singleton(object) + + single_object_1 = single_object() + single_object_2 = single_object() + + assert single_object_1 is single_object_2 + +Injections +---------- + +Injections are additional instructions, that are used for determining +dependencies of objects. + +Objects can take dependencies in various forms. Some objects take init +arguments, other are using attributes or methods to be initialized. Injection, +in terms of **Objects**, is an instruction how to provide dependency for the +particular object. + +Every Python object could be an injection's value. Special case is a **Objects** +provider as an injection's value. In such case, injection value is a result of +injectable provider call (every time injection is done). + +Injections are used by providers. + +.. code-block:: python + + """`KwArg` and `Attribute` injections example.""" + + import sqlite3 + + from objects.providers import Singleton + from objects.providers import NewInstance + + from objects.injections import KwArg + from objects.injections import Attribute + + + class ObjectA(object): + + """ObjectA has dependency on database.""" + + def __init__(self, database): + """Initializer. + + Database dependency need to be injected via init arg.""" + self.database = database + + def get_one(self): + """Select one from database and return it.""" + return self.database.execute('SELECT 1').fetchone()[0] + + + # Database and `ObjectA` providers. + database = Singleton(sqlite3.Connection, + KwArg('database', ':memory:'), + KwArg('timeout', 30), + KwArg('detect_types', True), + KwArg('isolation_level', 'EXCLUSIVE'), + Attribute('row_factory', sqlite3.Row)) + + object_a = NewInstance(ObjectA, + KwArg('database', database)) + + # Creating several `ObjectA` instances. + object_a_1 = object_a() + object_a_2 = object_a() + + # Making some asserts. + assert object_a_1 is not object_a_2 + assert object_a_1.database is object_a_2.database + assert object_a_1.get_one() == object_a_2.get_one() == 1 + +Catalogs +-------- + +Catalogs are named set of providers. + +**Objects** catalogs can be used for grouping of providers by some +kind of rules. In example below, there are two catalogs: +``Resources`` and ``Models``. + +``Resources`` catalog is used to group all common application resources like +database connection and various api clients, while ``Models`` catalog is used +for application model providers only. + +.. code-block:: python + + """Catalogs example.""" + + import sqlite3 + import httplib + + from objects.catalog import AbstractCatalog + + from objects.providers import Singleton + from objects.providers import NewInstance + + from objects.injections import KwArg + from objects.injections import Attribute + + + class SomeModel(object): + + """SomeModel has dependency on database and api client. + + Dependencies need to be injected via init args. + """ + + def __init__(self, database, api_client): + """Initializer.""" + self.database = database + self.api_client = api_client + + def api_request(self): + """Make api request.""" + self.api_client.request('GET', '/') + return self.api_client.getresponse() + + def get_one(self): + """Select one from database and return it.""" + return self.database.execute('SELECT 1').fetchone()[0] + + + class Resources(AbstractCatalog): + + """Resource providers catalog.""" + + database = Singleton(sqlite3.Connection, + KwArg('database', ':memory:'), + KwArg('timeout', 30), + KwArg('detect_types', True), + KwArg('isolation_level', 'EXCLUSIVE'), + Attribute('row_factory', sqlite3.Row)) + + api_client = Singleton(httplib.HTTPConnection, + KwArg('host', 'example.com'), + KwArg('port', 80), + KwArg('timeout', 10)) + + + class Models(AbstractCatalog): + + """Model providers catalog.""" + + some_model = NewInstance(SomeModel, + KwArg('database', Resources.database), + KwArg('api_client', Resources.api_client)) + + + # Creating `SomeModel` instance. + some_model = Models.some_model() + + # Making some asserts. + assert some_model.get_one() == 1 + assert some_model.api_request().status == 200 diff --git a/docs/examples.rst b/docs/examples.rst new file mode 100644 index 00000000..5b2b4fa2 --- /dev/null +++ b/docs/examples.rst @@ -0,0 +1,8 @@ +Examples +======== + +You can get more **Objects** examples in ``/examples`` directory on +GitHub: + + https://github.com/rmk135/objects + diff --git a/docs/feedback.rst b/docs/feedback.rst new file mode 100644 index 00000000..df25c405 --- /dev/null +++ b/docs/feedback.rst @@ -0,0 +1,9 @@ +Feedback +======== + +Feel free to post questions, bugs, feature requests, proposals etc. on +**Objects** GitHub Issues: + + https://github.com/rmk135/objects/issues + +Your feedback is quite important! diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 00000000..f472cb2f --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,44 @@ +Objects +======= + +Dependency management tool for Python projects. + ++---------------------------------------+-------------------------------------------------------------------+ +| *PyPi* | .. image:: https://pypip.in/version/Objects/badge.svg | +| | :target: https://pypi.python.org/pypi/Objects/ | +| | :alt: Latest Version | +| | .. image:: https://pypip.in/download/Objects/badge.svg | +| | :target: https://pypi.python.org/pypi/Objects/ | +| | :alt: Downloads | +| | .. image:: https://pypip.in/license/Objects/badge.svg | +| | :target: https://pypi.python.org/pypi/Objects/ | +| | :alt: License | ++---------------------------------------+-------------------------------------------------------------------+ +| *Python versions and implementations* | .. image:: https://pypip.in/py_versions/Objects/badge.svg | +| | :target: https://pypi.python.org/pypi/Objects/ | +| | :alt: Supported Python versions | +| | .. image:: https://pypip.in/implementation/Objects/badge.svg | +| | :target: https://pypi.python.org/pypi/Objects/ | +| | :alt: Supported Python implementations | ++---------------------------------------+-------------------------------------------------------------------+ +| *Builds and test coverage* | .. image:: https://travis-ci.org/rmk135/objects.svg?branch=master | +| | :target: https://travis-ci.org/rmk135/objects | +| | :alt: Build Status | +| | .. image:: https://coveralls.io/repos/rmk135/objects/badge.svg | +| | :target: https://coveralls.io/r/rmk135/objects | +| | :alt: Coverage Status | ++---------------------------------------+-------------------------------------------------------------------+ + + +Contents +-------- + +.. toctree:: + :maxdepth: 2 + + introduction + installation + entities + advanced_usage + examples + feedback diff --git a/docs/installation.rst b/docs/installation.rst new file mode 100644 index 00000000..046009bf --- /dev/null +++ b/docs/installation.rst @@ -0,0 +1,15 @@ +Installation +============ + +Latest stable version of **Objects** library can be installed from PyPi_:: + + pip install objects + +Sources can be cloned from GitHub_:: + + git clone https://github.com/rmk135/objects.git + +All **Objects** releases can be found on GitHub: https://github.com/rmk135/objects/releases + +.. _PyPi: https://pypi.python.org/pypi/Objects +.. _GitHub: https://github.com/rmk135/objects diff --git a/docs/introduction.rst b/docs/introduction.rst new file mode 100644 index 00000000..a60cd478 --- /dev/null +++ b/docs/introduction.rst @@ -0,0 +1,16 @@ +Introduction +============ + +Python ecosystem consists of a big amount of various classes, functions and +objects that could be used for applications development. Each of them has its +own role. + +Modern Python applications are mostly the composition of well-known open +source systems, frameworks, libraries and some turnkey functionality. + +When application goes bigger, its amount of objects and their dependencies +also increased extremely fast and became hard to maintain. + +**Objects** is designed to be developer's friendly tool for managing objects +and their dependencies in formal, pretty way. Main idea of **Objects** is to +keep dependencies under control. diff --git a/setup.py b/setup.py index 0946aa08..4a9f9142 100644 --- a/setup.py +++ b/setup.py @@ -6,13 +6,8 @@ from setuptools import setup # Getting description. -try: - import pypandoc -except (IOError, ImportError): - with open('README.md') as readme_file: - description = readme_file.read() -else: - description = pypandoc.convert('README.md', 'rst', format='markdown') +with open('README.rst') as readme_file: + description = readme_file.read() # Getting requirements.