[Python-modules-commits] [django-sitetree] 01/06: Import django-sitetree_1.7.0.orig.tar.gz

Michael Fladischer fladi at moszumanska.debian.org
Sun Dec 25 14:01:19 UTC 2016


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

fladi pushed a commit to branch master
in repository django-sitetree.

commit c70849aabf1d13a99b861419f172c9631ac26f7e
Author: Michael Fladischer <FladischerMichael at fladi.at>
Date:   Sun Dec 25 11:04:36 2016 +0100

    Import django-sitetree_1.7.0.orig.tar.gz
---
 .coveragerc                                        |   2 +-
 .travis.yml                                        |  17 +-
 AUTHORS                                            |   1 +
 CHANGELOG                                          |  14 +-
 INSTALL                                            |  31 +
 MANIFEST.in                                        |  14 +-
 README.rst                                         |  20 +-
 docs/source/index.rst                              |   3 +-
 docs/source/tags.rst                               |   6 +-
 setup.cfg                                          |   1 +
 setup.py                                           |   6 +
 sitetree/__init__.py                               |   4 +-
 sitetree/fields.py                                 |  18 +-
 .../management/commands/sitetree_resync_apps.py    |   2 +-
 sitetree/management/commands/sitetreeload.py       |  18 +-
 sitetree/runtests.py                               |  53 +-
 sitetree/settings.py                               |  17 +-
 sitetree/sitetreeapp.py                            | 740 ++++++++++------
 sitetree/templatetags/sitetree.py                  |  20 +-
 sitetree/tests.py                                  | 952 ---------------------
 sitetree/tests/__init__.py                         |   1 +
 sitetree/tests/common.py                           |  15 +
 sitetree/tests/conftest.py                         | 232 +++++
 sitetree/tests/sitetrees.py                        |  11 +
 sitetree/tests/test_admin.py                       | 124 +++
 sitetree/tests/test_dynamic.py                     |  87 ++
 sitetree/tests/test_forms.py                       |  40 +
 sitetree/tests/test_management.py                  |  87 ++
 sitetree/tests/test_models.py                      |  54 ++
 sitetree/tests/test_other.py                       |  62 ++
 sitetree/tests/test_templatetags.py                | 339 ++++++++
 sitetree/tests/test_utils.py                       | 122 +++
 sitetree/tests/urls.py                             |  13 +
 sitetree/utils.py                                  |  59 +-
 tox.ini                                            |  20 +-
 35 files changed, 1822 insertions(+), 1383 deletions(-)

diff --git a/.coveragerc b/.coveragerc
index 70d6a58..1104297 100644
--- a/.coveragerc
+++ b/.coveragerc
@@ -1,3 +1,3 @@
 [run]
 include = sitetree/*
-omit = sitetree/migrations/*, sitetree/south_migrations/*, sitetree/runtests.py, sitetree/tests.py, sitetree/config.py
+omit = sitetree/migrations/*, sitetree/south_migrations/*, sitetree/tests/*, sitetree/runtests.py, sitetree/config.py
diff --git a/.travis.yml b/.travis.yml
index 4f1bbed..751e85a 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -11,33 +11,22 @@ env:
   - DJANGO="Django>=1.9,<1.10"
   - DJANGO="Django>=1.8.6,<1.9"
   - DJANGO="Django>=1.7,<1.8"
-  - DJANGO="Django>=1.6,<1.7"
-  - DJANGO="Django>=1.4,<1.5"
-  
+
 install:
   - pip install -U coverage coveralls $DJANGO
-  
-script: coverage run -a --source=sitetree sitetree/runtests.py
+
+script: coverage run -a --source=sitetree setup.py test
 
 matrix:
 
   exclude:
    - python: 3.5
      env: DJANGO="Django>=1.7,<1.8"
-   - python: 3.5
-     env: DJANGO="Django>=1.6,<1.7"
-   - python: 3.5
-     env: DJANGO="Django>=1.4,<1.5"
-
-   - python: 3.4
-     env: DJANGO="Django>=1.4,<1.5"
 
    - python: 3.3
      env: DJANGO="Django>=1.10,<1.11"
    - python: 3.3
      env: DJANGO="Django>=1.9,<1.10"
-   - python: 3.3
-     env: DJANGO="Django>=1.4,<1.5"
 
 after_success:
   coveralls
diff --git a/AUTHORS b/AUTHORS
index e0f0120..2852226 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -36,6 +36,7 @@ Ben Cole <https://github.com/wengole>
 Vitaliy Ivanov <https://github.com/vit-ivanov>
 Sergey Maranchuk <https://github.com/slav0nic>
 Martey Dodoo <https://github.com/martey>
+Michał Suszko <https://github.com/msuszko>
 
 
 Translators
diff --git a/CHANGELOG b/CHANGELOG
index cee1011..96f4ea1 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -2,6 +2,18 @@ django-sitetree changelog
 =========================
 
 
+v1.7.0
+------
+* IMPORTANT: Caching reworked.
+* IMPORTANT: Dropped Django 1.5, 1.6 support (will not be tested anymore).
++ Added `ITEMS_FIELD_ROOT_ID` setting (see #205).
+* Reduced DB hits for trees with lots of permissions (see #213).
+* Improved `sitetreeload` command py3 compatibility (see #209).
+* Fixed `sitetreeload` unable to load some twisted tree structures (see #209).
+* Fixed `sitetree_resync_apps` run without args.
+* Fixed package distribution (see #222).
+
+
 v1.6.0
 ------
 + Prevent TreeItems from being their own parents (see #200).
@@ -272,4 +284,4 @@ v0.1.1
 
 v0.1.0
 ------
-+ Basic sitetree functionality.
++ Basic sitetree functionality.
\ No newline at end of file
diff --git a/INSTALL b/INSTALL
new file mode 100644
index 0000000..ab641cf
--- /dev/null
+++ b/INSTALL
@@ -0,0 +1,31 @@
+django-sitetree installation
+============================
+
+
+Python ``pip`` package is required to install ``django-sitetree``.
+
+
+From sources
+------------
+
+Use the following command line to install ``django-sitetree`` from sources directory (containing setup.py):
+
+    pip install .
+
+or
+
+    python setup.py install
+
+
+From PyPI
+---------
+
+Alternatively you can install ``django-sitetree`` from PyPI:
+
+    pip install django-sitetree
+
+
+Use `-U` flag for upgrade:
+
+    pip install -U django-sitetree
+
diff --git a/MANIFEST.in b/MANIFEST.in
index 21ad930..715c48c 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -4,12 +4,12 @@ include LICENSE
 include CHANGELOG
 
 include docs/Makefile
-recursive-include docs .rst
-recursive-include docs .py
+recursive-include docs *.rst
+recursive-include docs *.py
 
 recursive-include sitetree/locale *
-recursive-include sitetree/migrations .py
-recursive-include sitetree/south_migrations .py
-recursive-include sitetree/templates .html
-recursive-include sitetree/templatetags .py
-recursive-include sitetree/management .py
\ No newline at end of file
+recursive-include sitetree/migrations *.py
+recursive-include sitetree/south_migrations *.py
+recursive-include sitetree/templates *.html
+recursive-include sitetree/templatetags *.py
+recursive-include sitetree/management *.py
\ No newline at end of file
diff --git a/README.rst b/README.rst
index be98488..6cfaab0 100644
--- a/README.rst
+++ b/README.rst
@@ -2,23 +2,25 @@ django-sitetree
 ===============
 http://github.com/idlesign/django-sitetree
 
-.. image:: https://img.shields.io/pypi/v/django-sitetree.svg
-    :target: https://pypi.python.org/pypi/django-sitetree
+|release| |stats|  |lic| |ci| |coverage| |health|
 
-.. image:: https://img.shields.io/pypi/dm/django-sitetree.svg
+.. |release| image:: https://img.shields.io/pypi/v/django-sitetree.svg
     :target: https://pypi.python.org/pypi/django-sitetree
 
-.. image:: https://img.shields.io/pypi/l/django-sitetree.svg
+.. |stats| image:: https://img.shields.io/pypi/dm/django-sitetree.svg
     :target: https://pypi.python.org/pypi/django-sitetree
 
-.. image:: https://img.shields.io/coveralls/idlesign/django-sitetree/master.svg
-    :target: https://coveralls.io/r/idlesign/django-sitetree
+.. |lic| image:: https://img.shields.io/pypi/l/django-sitetree.svg
+    :target: https://pypi.python.org/pypi/django-sitetree
 
-.. image:: https://img.shields.io/travis/idlesign/django-sitetree/master.svg
+.. |ci| image:: https://img.shields.io/travis/idlesign/django-sitetree/master.svg
     :target: https://travis-ci.org/idlesign/django-sitetree
 
-.. image:: https://landscape.io/github/idlesign/django-sitetree/master/landscape.svg?style=flat
-   :target: https://landscape.io/github/idlesign/django-sitetree/master
+.. |coverage| image:: https://img.shields.io/coveralls/idlesign/django-sitetree/master.svg
+    :target: https://coveralls.io/r/idlesign/django-sitetree
+
+.. |health| image:: https://landscape.io/github/idlesign/django-sitetree/master/landscape.svg?style=flat
+    :target: https://landscape.io/github/idlesign/django-sitetree/master
 
 
 What's that
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 719f03a..b19d4ee 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -30,10 +30,9 @@ Requirements
 ------------
 
 1. Python 2.6+, 3.3+
-2. Django 1.4.2+
+2. Django 1.7+
 3. Auth Django contrib package
 4. Admin site Django contrib package (optional)
-5. South 1.0+ (for automatic DB migrations; not required for Django 1.7+)
 
 
 Table of Contents
diff --git a/docs/source/tags.rst b/docs/source/tags.rst
index df1797a..b85c49f 100644
--- a/docs/source/tags.rst
+++ b/docs/source/tags.rst
@@ -93,13 +93,17 @@ please use :ref:`tree hooks <tree-hooks>`.
       Bad
       Ugly
 
-  + **this-ancestor-children** - items under grandparent item (closest to root) for the item resolved as current for the current page.
+  + **this-parent-siblings** - items under parent item for the item resolved as current for the current page.
 
     Considering that we are now at `Public` renders::
 
       Web
       Postal
 
+  + **this-ancestor-children** - items under grandparent item (closest to root) for the item resolved as current for the current page.
+
+    Considering that we are now at `Public` renders all items under `Home` (which is closest to the root).
+
   Thus in the template tag example above `trunk` is reserved alias, and `topmenu` alias is given to an item through
   admin site.
 
diff --git a/setup.cfg b/setup.cfg
index 8b8c1dc..58cd3db 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,6 @@
 [aliases]
 release = clean --all sdist bdist_wheel upload
+test = pytest
 
 [wheel]
 universal = 1
diff --git a/setup.py b/setup.py
index 9de2c87..507e68d 100755
--- a/setup.py
+++ b/setup.py
@@ -1,4 +1,5 @@
 import os
+import sys
 from setuptools import setup
 from sitetree import VERSION
 
@@ -6,6 +7,8 @@ f = open(os.path.join(os.path.dirname(__file__), 'README.rst'))
 readme = f.read()
 f.close()
 
+PYTEST_RUNNER = ['pytest-runner'] if 'test' in sys.argv else []
+
 setup(
     name='django-sitetree',
     version='.'.join(map(str, VERSION)),
@@ -22,6 +25,9 @@ setup(
     include_package_data=True,
     zip_safe=False,
 
+    setup_requires=[] + PYTEST_RUNNER,
+    tests_require=['pytest', 'pytest-django'],
+
     classifiers=[
         'Development Status :: 5 - Production/Stable',
         'Environment :: Web Environment',
diff --git a/sitetree/__init__.py b/sitetree/__init__.py
index 2d28686..ae3dfcc 100644
--- a/sitetree/__init__.py
+++ b/sitetree/__init__.py
@@ -1,4 +1,4 @@
-VERSION = (1, 6, 0)
+VERSION = (1, 7, 0)
 
 
-default_app_config = 'sitetree.config.SitetreeConfig'
+default_app_config = 'sitetree.config.SitetreeConfig'
\ No newline at end of file
diff --git a/sitetree/fields.py b/sitetree/fields.py
index 05df777..f9fd3d9 100644
--- a/sitetree/fields.py
+++ b/sitetree/fields.py
@@ -6,6 +6,7 @@ from django.utils.safestring import mark_safe
 
 from .templatetags.sitetree import sitetree_tree
 from .utils import get_tree_model, get_tree_item_model
+from .settings import ITEMS_FIELD_ROOT_ID
 
 
 MODEL_TREE_CLASS = get_tree_model()
@@ -40,23 +41,28 @@ class TreeItemChoiceField(ChoiceField):
         tree_token = u'sitetree_tree from "%s" template "%s"' % (self.tree, self.template)
 
         context_kwargs = {'current_app': 'admin'}
-        if VERSION >= (1, 8):
-            context = template.Context(context_kwargs)
-        else:
-            context = template.Context(**context_kwargs)
+        context = template.Context(context_kwargs) if VERSION >= (1, 8) else template.Context(**context_kwargs)
+        context.update({'request': object()})
 
         choices_str = sitetree_tree(
             Parser(None), Token(token_type=TOKEN_BLOCK, contents=tree_token)
         ).render(context)
 
-        tree_choices = [('', self.root_title)]
+        tree_choices = [(ITEMS_FIELD_ROOT_ID, self.root_title)]
+
         for line in choices_str.splitlines():
             if line.strip():
                 splitted = line.split(':::')
                 tree_choices.append((splitted[0], mark_safe(splitted[1])))
+
         return tree_choices
 
     def clean(self, value):
         if not value:
             return None
-        return MODEL_TREE_ITEM_CLASS.objects.get(pk=value)
+
+        try:
+            return MODEL_TREE_ITEM_CLASS.objects.get(pk=value)
+
+        except MODEL_TREE_ITEM_CLASS.DoesNotExist:
+            return None
diff --git a/sitetree/management/commands/sitetree_resync_apps.py b/sitetree/management/commands/sitetree_resync_apps.py
index 0a06135..129bb01 100644
--- a/sitetree/management/commands/sitetree_resync_apps.py
+++ b/sitetree/management/commands/sitetree_resync_apps.py
@@ -28,7 +28,7 @@ class Command(BaseCommand):
     option_list = get_options()
 
     def add_arguments(self, parser):
-        parser.add_argument('args', metavar='app', nargs='+', help='Application names.')
+        parser.add_argument('args', metavar='app', nargs='*', help='Application names.')
         get_options(parser.add_argument)
 
     def handle(self, *apps, **options):
diff --git a/sitetree/management/commands/sitetreeload.py b/sitetree/management/commands/sitetreeload.py
index a90c089..68b0df4 100644
--- a/sitetree/management/commands/sitetreeload.py
+++ b/sitetree/management/commands/sitetreeload.py
@@ -30,7 +30,7 @@ get_options = options_getter((
         help='Mode to put data into DB. Variants: `replace`, `append`.'),
 
     CommandOption(
-        '--items_into_tree', action='store', dest='into_tree', default=None,
+        '--items_into_tree', action='store', dest='items_into_tree', default=None,
         help='Import only tree items data into tree with given alias.'),
 ))
 
@@ -50,7 +50,7 @@ class Command(BaseCommand):
 
         using = options.get('database', DEFAULT_DB_ALIAS)
         mode = options.get('mode', 'append')
-        items_into_tree = options.get('into_tree', None)
+        items_into_tree = options.get('items_into_tree', None)
 
         if items_into_tree is not None:
             try:
@@ -78,11 +78,8 @@ class Command(BaseCommand):
         loaded_object_count = 0
 
         if mode == 'replace':
-            try:
-                MODEL_TREE_CLASS.objects.all().delete()
-                MODEL_TREE_ITEM_CLASS.objects.all().delete()
-            except ObjectDoesNotExist:
-                pass
+            MODEL_TREE_CLASS.objects.all().delete()
+            MODEL_TREE_ITEM_CLASS.objects.all().delete()
 
         for fixture_file in fixture_files:
 
@@ -137,6 +134,9 @@ class Command(BaseCommand):
 
                     parents_ahead = []
 
+                    # Parents go first: enough for simple cases.
+                    tree_items[orig_tree_id].sort(key=lambda item: item.id not in tree_item_parents.keys())
+
                     for tree_item in tree_items[orig_tree_id]:
                         parent_ahead = False
                         self.stdout.write('Importing item `%s` ...\n' % tree_item.title)
@@ -182,9 +182,7 @@ class Command(BaseCommand):
 
                 self.stderr.write(
                     self.style.ERROR('Fixture `%s` import error: %s\n' % (
-                        fixture_file, ''.join(traceback.format_exception(
-                            sys.exc_type, sys.exc_value, sys.exc_traceback
-                        ))
+                        fixture_file, ''.join(traceback.format_exception(*sys.exc_info()))
                     ))
                 )
 
diff --git a/sitetree/runtests.py b/sitetree/runtests.py
index 27970d5..be464ba 100755
--- a/sitetree/runtests.py
+++ b/sitetree/runtests.py
@@ -1,54 +1,5 @@
 #! /usr/bin/env python
-import sys
-import os
-
-from django.conf import settings, global_settings
-
-
-APP_NAME = 'sitetree'
-
-
-def main():
-    sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
-
-    if not settings.configured:
-
-        configure_kwargs = dict(
-            INSTALLED_APPS=('django.contrib.auth', 'django.contrib.contenttypes', APP_NAME),
-            DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3'}},
-            ROOT_URLCONF='sitetree.tests',
-            MIDDLEWARE_CLASSES=global_settings.MIDDLEWARE_CLASSES,  # Prevents Django 1.7 warning.
-        )
-
-        try:
-            configure_kwargs['TEMPLATE_CONTEXT_PROCESSORS'] = tuple(global_settings.TEMPLATE_CONTEXT_PROCESSORS) + (
-                'django.core.context_processors.request',
-            )
-
-        except AttributeError:
-
-            # Django 1.8+
-            configure_kwargs['TEMPLATES'] = [
-                {
-                    'BACKEND': 'django.template.backends.django.DjangoTemplates',
-                    'APP_DIRS': True,
-                },
-            ]
-
-        settings.configure(**configure_kwargs)
-
-    try:  # Django 1.7 +
-        from django import setup
-        setup()
-    except ImportError:
-        pass
-
-    from django.test.utils import get_runner
-    runner = get_runner(settings)()
-    failures = runner.run_tests((APP_NAME,))
-
-    sys.exit(failures)
-
 
 if __name__ == '__main__':
-    main()
+    from pytest import main as pytest_main
+    pytest_main()
diff --git a/sitetree/settings.py b/sitetree/settings.py
index 218bde5..492c559 100644
--- a/sitetree/settings.py
+++ b/sitetree/settings.py
@@ -2,17 +2,30 @@ from django.conf import settings
 
 
 MODEL_TREE = getattr(settings, 'SITETREE_MODEL_TREE', 'sitetree.Tree')
+"""Path to a tree model (app.class)."""
+
 MODEL_TREE_ITEM = getattr(settings, 'SITETREE_MODEL_TREE_ITEM', 'sitetree.TreeItem')
+"""Path to a tree item model (app.class)."""
 
 APP_MODULE_NAME = getattr(settings, 'SITETREE_APP_MODULE_NAME', 'sitetrees')
+"""Module name where applications store trees shipped with them."""
 
 UNRESOLVED_ITEM_MARKER = getattr(settings, 'SITETREE_UNRESOLVED_ITEM_MARKER', u'#unresolved')
+"""This string is place instead of item URL if actual URL cannot be resolved."""
 
 RAISE_ITEMS_ERRORS_ON_DEBUG = getattr(settings, 'SITETREE_RAISE_ITEMS_ERRORS_ON_DEBUG', True)
 
-# Sitetree objects are stored in Django cache for a year (60 * 60 * 24 * 365 = 31536000 sec).
-# Cache is only invalidated on sitetree or sitetree item change.
+ITEMS_FIELD_ROOT_ID = getattr(settings, 'SITETREE_ITEMS_FIELD_ROOT_ID', '')
+"""Item ID to be used for root item in TreeItemChoiceField.
+This is adjustable to be able to workaround client-side field validation issues in thirdparties.
+
+"""
+
 CACHE_TIMEOUT = getattr(settings, 'SITETREE_CACHE_TIMEOUT', 31536000)
+"""Sitetree objects are stored in Django cache for a year (60 * 60 * 24 * 365 = 31536000 sec).
+Cache is only invalidated on sitetree or sitetree item change.
+
+"""
 
 # Reserved tree items aliases.
 ALIAS_TRUNK = 'trunk'
diff --git a/sitetree/sitetreeapp.py b/sitetree/sitetreeapp.py
index e387106..90e9e95 100644
--- a/sitetree/sitetreeapp.py
+++ b/sitetree/sitetreeapp.py
@@ -1,9 +1,7 @@
 from __future__ import unicode_literals
-
 import warnings
-
 from collections import defaultdict
-from copy import copy, deepcopy
+from copy import deepcopy
 from threading import local
 from functools import partial
 
@@ -15,7 +13,6 @@ from django.utils import six
 from django.utils.http import urlquote
 from django.utils.translation import get_language
 from django.utils.encoding import python_2_unicode_compatible
-from django.template import Context
 from django.template.loader import get_template
 from django.template.base import (
     FilterExpression, Lexer, Parser, Token, Variable, VariableDoesNotExist, TOKEN_BLOCK, UNKNOWN_SOURCE, TOKEN_TEXT,
@@ -29,6 +26,11 @@ from .settings import (
 from .exceptions import SiteTreeError
 
 
+if False:  # For type hinting purposes.
+    from django.template import Context
+    from .models import TreeItemBase
+
+
 if VERSION >= (1, 9, 0):
     get_lexer = partial(Lexer)
 else:
@@ -39,36 +41,44 @@ MODEL_TREE_CLASS = get_tree_model()
 MODEL_TREE_ITEM_CLASS = get_tree_item_model()
 
 
-# Holds tree items processor callable or None.
 _ITEMS_PROCESSOR = None
-# Holds aliases of trees that support internationalization.
+"""Stores tree items processor callable or None."""
+
 _I18N_TREES = []
-# Holds trees dynamically loaded from project apps.
+"""Stores aliases of trees supporting internationalization."""
+
 _DYNAMIC_TREES = {}
-# Dictionary index in `_DYNAMIC_TREES` for orphaned trees list.
+"""Holds trees dynamically loaded from project apps."""
+
 _IDX_ORPHAN_TREES = 'orphans'
-# Dictinary index name template in `_DYNAMIC_TREES`.
+"""Dictionary index in `_DYNAMIC_TREES` for orphaned trees list."""
+
 _IDX_TPL = '%s|:|%s'
-# SiteTree app-wise object.
-_SITETREE = None
+"""Name template used as dictionary index in `_DYNAMIC_TREES`."""
 
 _THREAD_LOCAL = local()
-_THREAD_LANG = 'sitetree_lang'
-_THREAD_CACHE = 'sitetree_cache'
+_THREAD_SITETREE = 'sitetree'
+
+_URL_TAG_NEW_STYLE = VERSION >= (1, 5, 0)
+
+_UNSET = set()  # Sentinel
 
 
 def get_sitetree():
-    """Returns SiteTree [singleton] object, implementing utility methods.
+    """Returns SiteTree (thread-singleton) object, implementing utility methods.
 
-    :return: SiteTree
+    :rtype: SiteTree
     """
-    global _SITETREE
-    if _SITETREE is None:
-        _SITETREE = SiteTree()
-    return _SITETREE
+    sitetree = getattr(_THREAD_LOCAL, _THREAD_SITETREE, None)
+
+    if sitetree is None:
+        sitetree = SiteTree()
+        setattr(_THREAD_LOCAL, _THREAD_SITETREE, sitetree)
+
+    return sitetree
 
 
-def register_items_hook(callable):
+def register_items_hook(func):
     """Registers a hook callable to process tree items right before they are passed to templates.
 
     Callable should be able to:
@@ -101,9 +111,10 @@ def register_items_hook(callable):
         # And we register items processor.
         register_items_hook(my_items_processor)
 
+    :param func:
     """
     global _ITEMS_PROCESSOR
-    _ITEMS_PROCESSOR = callable
+    _ITEMS_PROCESSOR = func
 
 
 def register_i18n_trees(aliases):
@@ -129,6 +140,7 @@ def register_i18n_trees(aliases):
         # At last we register i18n trees.
         register_i18n_trees(['my_tree', 'my_another_tree'])
 
+    :param aliases:
     """
     global _I18N_TREES
     _I18N_TREES = aliases
@@ -163,10 +175,9 @@ def register_dynamic_trees(trees, *args, **kwargs):
         )
 
     Accepted kwargs:
+        bool reset_cache: Resets tree cache, to introduce all changes made to a tree immediately.
 
-    :param bool reset_cache: Resets tree cache, to introduce all changes made to a tree immediately.
     """
-
     global _DYNAMIC_TREES
 
     if _IDX_ORPHAN_TREES not in _DYNAMIC_TREES:
@@ -193,9 +204,9 @@ def register_dynamic_trees(trees, *args, **kwargs):
 
     reset_cache = kwargs.get('reset_cache', False)
     if reset_cache:
-        cache = get_sitetree().cache
-        cache.empty()
-        cache.reset()
+        cache_ = get_sitetree().cache
+        cache_.empty()
+        cache_.reset()
 
 
 def get_dynamic_trees():
@@ -207,38 +218,40 @@ def compose_dynamic_tree(src, target_tree_alias=None, parent_tree_item_alias=Non
     """Returns a structure describing a dynamic sitetree.utils
     The structure can be built from various sources,
 
-    Thus, if a string is passed to `src`, it'll be treated as the name of an app,
-    from where one want to import sitetrees definitions.
-
-    On the other hand, `src` can be an iterable of trees definitions
-    (see `sitetree.utils.tree()` and `item()` functions).
+    :param str|iterable src: If a string is passed to `src`, it'll be treated as the name of an app,
+        from where one want to import sitetrees definitions. `src` can be an iterable
+        of tree definitions (see `sitetree.toolbox.tree()` and `item()` functions).
 
+    :param str|unicode target_tree_alias: Static tree alias to attach items from dynamic trees to.
 
-    `target_tree_alias` - expects a static tree alias to attach items from dynamic trees to.
-    `parent_tree_item_alias` - expects a tree item alias from a static tree to attach items from dynamic trees to.
-    `include_trees` - expects a list of sitetree aliases to filter `src`.
+    :param str|unicode parent_tree_item_alias: Tree item alias from a static tree to attach items from dynamic trees to.
 
+    :param list include_trees: Sitetree aliases to filter `src`.
 
+    :rtype: dict
     """
-
     def result(sitetrees=src):
         if include_trees is not None:
             sitetrees = [tree for tree in sitetrees if tree.alias in include_trees]
-        return {'app': src, 'sitetrees': sitetrees, 'tree': target_tree_alias, 'parent_item': parent_tree_item_alias}
+
+        return {
+            'app': src,
+            'sitetrees': sitetrees,
+            'tree': target_tree_alias,
+            'parent_item': parent_tree_item_alias}
 
     if isinstance(src, six.string_types):
-        # Considered an application name.
+        # Considered to be an application name.
         try:
             module = import_app_sitetree_module(src)
-            if module is None:
-                return None
-            return result(getattr(module, 'sitetrees', None))
+            return None if module is None else result(getattr(module, 'sitetrees', None))
+
         except ImportError as e:
             if settings.DEBUG:
                 warnings.warn('Unable to register dynamic sitetree(s) for `%s` application: %s. ' % (src, e))
-    else:
-        return result()
-    return None
+            return None
+
+    return result()
 
 
 @python_2_unicode_compatible
@@ -247,6 +260,9 @@ class LazyTitle(object):
     Produces resolved title as unicode representation."""
 
     def __init__(self, title):
+        """
+        :param str|unicode title:
+        """
         self.title = title
 
     def __str__(self):
@@ -259,7 +275,7 @@ class LazyTitle(object):
                 my_tokens.remove(my_token)
 
         my_parser = Parser(my_tokens)
-        return my_parser.parse().render(SiteTree.get_global_context())
+        return my_parser.parse().render(get_sitetree().current_page_context)
 
     def __eq__(self, other):
         return self.__str__() == other
@@ -278,6 +294,7 @@ class Cache(object):
         signals.post_delete.connect(cache_empty, sender=MODEL_TREE_ITEM_CLASS)
         # Listen to the changes in item permissions table.
         signals.m2m_changed.connect(cache_empty, sender=MODEL_TREE_ITEM_CLASS.access_permissions)
+        self.init()
 
     @classmethod
     def reset(cls):
@@ -292,18 +309,10 @@ class Cache(object):
         """Initializes local cache from Django cache."""
 
         # Drop cache flag set by .reset() method.
-        cache.get('sitetrees_reset') and self.empty()
-
-        cache_ = getattr(_THREAD_LOCAL, _THREAD_CACHE, None)
-        if cache_ is None:
-
-            cache_ = cache.get(
-                # Init cache dictionary with predefined entries.
-                'sitetrees', {'sitetrees': {}, 'urls': {}, 'parents': {}, 'items_by_ids': {}, 'tree_aliases': {}})
+        cache.get('sitetrees_reset') and self.empty(init=False)
 
-            setattr(_THREAD_LOCAL, _THREAD_CACHE, cache_)
-
-        self.cache = cache_
+        self.cache = cache.get(
+            'sitetrees', {'sitetrees': {}, 'parents': {}, 'items_by_ids': {}, 'tree_aliases': {}})
 
     def save(self):
         """Saves sitetree data to Django cache."""
@@ -311,61 +320,85 @@ class Cache(object):
 
     def empty(self, **kwargs):
         """Empties cached sitetree data."""
-        self.cache = None
-        setattr(_THREAD_LOCAL, _THREAD_CACHE, None)
         cache.delete('sitetrees')
         cache.delete('sitetrees_reset')
 
+        kwargs.get('init', True) and self.init()
+
     def get_entry(self, entry_name, key):
-        """Returns cache entry parameter value by its name."""
+        """Returns cache entry parameter value by its name.
+
+        :param str|unicode entry_name:
+        :param key:
+        :return:
+        """
         return self.cache[entry_name].get(key, False)
 
     def update_entry_value(self, entry_name, key, value):
-        """Updates cache entry parameter with new data."""
+        """Updates cache entry parameter with new data.
+
+        :param str|unicode entry_name:
+        :param key:
+        :param value:
+        """
         if key not in self.cache[entry_name]:
             self.cache[entry_name][key] = {}
+
         self.cache[entry_name][key].update(value)
 
     def set_entry(self, entry_name, key, value):
-        """Replaces entire cache entry parameter data by its name with new data."""
+        """Replaces entire cache entry parameter data by its name with new data.
+
+        :param str|unicode entry_name:
+        :param key:
+        :param value:
+        """
         self.cache[entry_name][key] = value
 
 
 class SiteTree(object):
-
-    _global_context = Context()
+    """Main logic handler."""
 
     def __init__(self):
-        self.cache = Cache()
+        self.init(context=None)
 
-    @classmethod
-    def set_global_context(cls, context):
-        """Saves context as global context if not already set or if changed.
-        Almost all variables are resolved against global context.
+    def init(self, context):
+        """Initializes sitetree to handle new request.
 
+        :param Context|None context:
         """
-        if not cls._global_context or id(context) != id(cls._global_context):
-            cls._global_context = context
+        self.cache = Cache()
+        self.current_page_context = context
+        self.current_request = context.get('request', None) if context else None
+        self.current_lang = get_language()
 
-    @classmethod
-    def get_global_context(cls):
-        """Returns current sitetree global context."""
-        return cls._global_context
+        self._current_app_is_admin = None
+        self._current_user_permissions = _UNSET
+        self._items_urls = {}  # Resolved urls are cache for a request.
+        self._current_items = {}
 
     def resolve_tree_i18n_alias(self, alias):
         """Resolves internationalized tree alias.
         Verifies whether a separate sitetree is available for currently active language.
         If so, returns i18n alias. If not, returns the initial alias.
+
+        :param str|unicode alias:
+        :rtype: str|unicode
         """
-        if alias in _I18N_TREES:
-            current_language_code = self.lang_get().replace('_', '-').split('-')[0]
-            i18n_tree_alias = '%s_%s' % (alias, current_language_code)
-            trees_count = self.cache.get_entry('tree_aliases', i18n_tree_alias)
-            if trees_count is False:
-                trees_count = MODEL_TREE_CLASS.objects.filter(alias=i18n_tree_alias).count()
-                self.cache.set_entry('tree_aliases', i18n_tree_alias, trees_count)
-            if trees_count:
-                alias = i18n_tree_alias
+        if alias not in _I18N_TREES:
+            return alias
+
+        current_language_code = self.current_lang.replace('_', '-').split('-')[0]
+        i18n_tree_alias = '%s_%s' % (alias, current_language_code)
+        trees_count = self.cache.get_entry('tree_aliases', i18n_tree_alias)
+
+        if trees_count is False:
+            trees_count = MODEL_TREE_CLASS.objects.filter(alias=i18n_tree_alias).count()
+            self.cache.set_entry('tree_aliases', i18n_tree_alias, trees_count)
+
+        if trees_count:
+            alias = i18n_tree_alias
+
         return alias
 
     @staticmethod
@@ -373,18 +406,21 @@ class SiteTree(object):
         """Attaches dynamic sitetrees items registered with `register_dynamic_trees()`
         to an initial (source) items list.
 
+        :param str|unicode tree_alias:
+        :param list src_tree_items:
+        :rtype: list
         """
         if not _DYNAMIC_TREES:
             return src_tree_items
 
         # This guarantees that a dynamic source stays intact,
         # no matter how dynamic sitetrees are attached.
-        TREES = deepcopy(_DYNAMIC_TREES)
+        trees = deepcopy(_DYNAMIC_TREES)
 
         items = []
         if not src_tree_items:
-            if _IDX_ORPHAN_TREES in TREES and tree_alias in TREES[_IDX_ORPHAN_TREES]:
-                for tree in TREES[_IDX_ORPHAN_TREES][tree_alias]:
+            if _IDX_ORPHAN_TREES in trees and tree_alias in trees[_IDX_ORPHAN_TREES]:
+                for tree in trees[_IDX_ORPHAN_TREES][tree_alias]:
                     items.extend(tree.dynamic_items)
         else:
 
@@ -393,67 +429,87 @@ class SiteTree(object):
             # Tree item attachment by alias.
             for static_item in list(src_tree_items):
                 items.append(static_item)
-                if static_item.alias:
-                    idx = _IDX_TPL % (tree_alias, static_item.alias)
-                    if idx in TREES:
-                        for tree in TREES[idx]:
-                            tree.alias = tree_alias
-                            for dyn_item in tree.dynamic_items:
-                                if dyn_item.parent is None:
-                                    dyn_item.parent = static_item
-                                # Unique IDs are required for the same trees attached
-                                # to different parents.
-                                dyn_item.id = generate_id_for(dyn_item)
-                                items.append(dyn_item)
+                if not static_item.alias:
+                    continue
+
+                idx = _IDX_TPL % (tree_alias, static_item.alias)
+                if idx not in trees:
+                    continue
+
+                for tree in trees[idx]:
+                    tree.alias = tree_alias
+                    for dyn_item in tree.dynamic_items:
+                        if dyn_item.parent is None:
+                            dyn_item.parent = static_item
+                        # Unique IDs are required for the same trees attached
+                        # to different parents.
+                        dyn_item.id = generate_id_for(dyn_item)
+                        items.append(dyn_item)
 
             # Tree root attachment.
             idx = _IDX_TPL % (tree_alias, None)
             if idx in _DYNAMIC_TREES:
-                TREES = deepcopy(_DYNAMIC_TREES)
-                for tree in TREES[idx]:
+                trees = deepcopy(_DYNAMIC_TREES)
+                for tree in trees[idx]:
                     tree.alias = tree_alias
                     items.extend(tree.dynamic_items)
 
         return items
 
     def current_app_is_admin(self):
-        """Returns boolean whether current application is Admin contrib."""
-        global_context = self._global_context
+        """Returns boolean whether current application is Admin contrib.
+
+        :rtype: bool
+        """
+        is_admin = self._current_app_is_admin
+        if is_admin is None:
+            context = self.current_page_context
+
+            current_app = getattr(
+                # Try from request.resolver_match.app_name
+                getattr(context.get('request', None), 'resolver_match', None), 'app_name',
+                # Try from global context obj.
+                getattr(context, 'current_app', None))
 
-        current_app = getattr(
-            # Try from request.resolver_match.app_name
-            getattr(global_context.get('request', None), 'resolver_match', None), 'app_name',
-            # Try from global context obj.
-            getattr(global_context, 'current_app', None))
+            if current_app is None:  # Try from global context dict.
+                current_app = context.get('current_app', '')
 
-        if current_app is None:  # Try from global context dict.
... 3210 lines suppressed ...

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



More information about the Python-modules-commits mailing list