Skip to content

Commit 9c4f772

Browse files
committed
Initial commit under morepath name.
0 parents  commit 9c4f772

18 files changed

Lines changed: 898 additions & 0 deletions

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
bin
2+
parts
3+
.installed.cfg
4+
develop-eggs
5+
src
6+
.mr.developer.cfg

LICENSE.txt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Copyright (c) 2010-2013, Morepath Developers
2+
All rights reserved.
3+
4+
Redistribution and use in source and binary forms, with or without
5+
modification, are permitted provided that the following conditions are met:
6+
* Redistributions of source code must retain the above copyright
7+
notice, this list of conditions and the following disclaimer.
8+
* Redistributions in binary form must reproduce the above copyright
9+
notice, this list of conditions and the following disclaimer in the
10+
documentation and/or other materials provided with the distribution.
11+
* Neither the name of the <organization> nor the
12+
names of its contributors may be used to endorse or promote products
13+
derived from this software without specific prior written permission.
14+
15+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18+
DISCLAIMED. IN NO EVENT SHALL FANSTATIC DEVELOPERS BE LIABLE FOR ANY
19+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

bootstrap.py

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
##############################################################################
2+
#
3+
# Copyright (c) 2006 Zope Foundation and Contributors.
4+
# All Rights Reserved.
5+
#
6+
# This software is subject to the provisions of the Zope Public License,
7+
# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
8+
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9+
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10+
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11+
# FOR A PARTICULAR PURPOSE.
12+
#
13+
##############################################################################
14+
"""Bootstrap a buildout-based project
15+
16+
Simply run this script in a directory containing a buildout.cfg.
17+
The script accepts buildout command-line options, so you can
18+
use the -c option to specify an alternate configuration file.
19+
"""
20+
21+
import os
22+
import shutil
23+
import sys
24+
import tempfile
25+
26+
from optparse import OptionParser
27+
28+
tmpeggs = tempfile.mkdtemp()
29+
30+
usage = '''\
31+
[DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options]
32+
33+
Bootstraps a buildout-based project.
34+
35+
Simply run this script in a directory containing a buildout.cfg, using the
36+
Python that you want bin/buildout to use.
37+
38+
Note that by using --find-links to point to local resources, you can keep
39+
this script from going over the network.
40+
'''
41+
42+
parser = OptionParser(usage=usage)
43+
parser.add_option("-v", "--version", help="use a specific zc.buildout version")
44+
45+
parser.add_option("-t", "--accept-buildout-test-releases",
46+
dest='accept_buildout_test_releases',
47+
action="store_true", default=False,
48+
help=("Normally, if you do not specify a --version, the "
49+
"bootstrap script and buildout gets the newest "
50+
"*final* versions of zc.buildout and its recipes and "
51+
"extensions for you. If you use this flag, "
52+
"bootstrap and buildout will get the newest releases "
53+
"even if they are alphas or betas."))
54+
parser.add_option("-c", "--config-file",
55+
help=("Specify the path to the buildout configuration "
56+
"file to be used."))
57+
parser.add_option("-f", "--find-links",
58+
help=("Specify a URL to search for buildout releases"))
59+
60+
61+
options, args = parser.parse_args()
62+
63+
######################################################################
64+
# load/install setuptools
65+
66+
to_reload = False
67+
try:
68+
import pkg_resources
69+
import setuptools
70+
except ImportError:
71+
ez = {}
72+
73+
try:
74+
from urllib.request import urlopen
75+
except ImportError:
76+
from urllib2 import urlopen
77+
78+
# XXX use a more permanent ez_setup.py URL when available.
79+
exec(urlopen('https://bitbucket.org/pypa/setuptools/raw/0.7.2/ez_setup.py'
80+
).read(), ez)
81+
setup_args = dict(to_dir=tmpeggs, download_delay=0)
82+
ez['use_setuptools'](**setup_args)
83+
84+
if to_reload:
85+
reload(pkg_resources)
86+
import pkg_resources
87+
# This does not (always?) update the default working set. We will
88+
# do it.
89+
for path in sys.path:
90+
if path not in pkg_resources.working_set.entries:
91+
pkg_resources.working_set.add_entry(path)
92+
93+
######################################################################
94+
# Install buildout
95+
96+
ws = pkg_resources.working_set
97+
98+
cmd = [sys.executable, '-c',
99+
'from setuptools.command.easy_install import main; main()',
100+
'-mZqNxd', tmpeggs]
101+
102+
find_links = os.environ.get(
103+
'bootstrap-testing-find-links',
104+
options.find_links or
105+
('http://downloads.buildout.org/'
106+
if options.accept_buildout_test_releases else None)
107+
)
108+
if find_links:
109+
cmd.extend(['-f', find_links])
110+
111+
setuptools_path = ws.find(
112+
pkg_resources.Requirement.parse('setuptools')).location
113+
114+
requirement = 'zc.buildout'
115+
version = options.version
116+
if version is None and not options.accept_buildout_test_releases:
117+
# Figure out the most recent final version of zc.buildout.
118+
import setuptools.package_index
119+
_final_parts = '*final-', '*final'
120+
121+
def _final_version(parsed_version):
122+
for part in parsed_version:
123+
if (part[:1] == '*') and (part not in _final_parts):
124+
return False
125+
return True
126+
index = setuptools.package_index.PackageIndex(
127+
search_path=[setuptools_path])
128+
if find_links:
129+
index.add_find_links((find_links,))
130+
req = pkg_resources.Requirement.parse(requirement)
131+
if index.obtain(req) is not None:
132+
best = []
133+
bestv = None
134+
for dist in index[req.project_name]:
135+
distv = dist.parsed_version
136+
if _final_version(distv):
137+
if bestv is None or distv > bestv:
138+
best = [dist]
139+
bestv = distv
140+
elif distv == bestv:
141+
best.append(dist)
142+
if best:
143+
best.sort()
144+
version = best[-1].version
145+
if version:
146+
requirement = '=='.join((requirement, version))
147+
cmd.append(requirement)
148+
149+
import subprocess
150+
if subprocess.call(cmd, env=dict(os.environ, PYTHONPATH=setuptools_path)) != 0:
151+
raise Exception(
152+
"Failed to execute command:\n%s",
153+
repr(cmd)[1:-1])
154+
155+
######################################################################
156+
# Import and run buildout
157+
158+
ws.add_entry(tmpeggs)
159+
ws.require(requirement)
160+
import zc.buildout.buildout
161+
162+
if not [a for a in args if '=' not in a]:
163+
args.append('bootstrap')
164+
165+
# if -c was provided, we push it back into args for buildout' main function
166+
if options.config_file is not None:
167+
args[0:0] = ['-c', options.config_file]
168+
169+
zc.buildout.buildout.main(args)
170+
shutil.rmtree(tmpeggs)

buildout.cfg

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
[buildout]
2+
develop = .
3+
parts = devpython scripts
4+
versions = versions
5+
show-picked-versions = true
6+
extensions = mr.developer
7+
auto-checkout = comparch
8+
9+
[versions]
10+
11+
[sources]
12+
comparch = git ssh://faassen@startifact.com/home/faassen/comparch.git
13+
14+
[devpython]
15+
recipe = zc.recipe.egg
16+
interpreter = devpython
17+
eggs = morepath
18+
pyflakes
19+
20+
[scripts]
21+
recipe = zc.recipe.egg:scripts
22+
eggs = morepath [test]
23+
pytest

morepath/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .implicit import initialize
2+
3+
initialize()

morepath/configure.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
from comparch import PredicateRegistry
2+
3+
from .interfaces import IResource, ResourceError
4+
from .request import Request
5+
6+
# XXX hardcoded predicates gotta change
7+
PREDICATES = ['name', 'request_method']
8+
9+
class PredicateLookup(object):
10+
def __init__(self, predicate_registry):
11+
self.predicate_registry = predicate_registry
12+
13+
def __call__(self, request, model):
14+
func = self.predicate_registry.get(self.get_predicates(request))
15+
if func is None:
16+
return None
17+
return func(request, model)
18+
19+
# XXX move to request?
20+
def get_predicates(self, request):
21+
result = {}
22+
result['request_method'] = request.method
23+
result['name'] = request.resolver_info()['name']
24+
return result
25+
26+
def register_resource(registry, model, resource, **predicates):
27+
lookup = registry.exact_get(IResource, (Request, model))
28+
if lookup is None:
29+
lookup = PredicateLookup(PredicateRegistry(PREDICATES))
30+
registry.register(IResource, (Request, model), lookup)
31+
lookup.predicate_registry.register(predicates, resource)
32+

morepath/implicit.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from comparch import ClassRegistry, CachedLookup, implicit
2+
3+
def initialize():
4+
registry = ClassRegistry()
5+
implicit.initialize(CachedLookup(registry))

morepath/interfaces.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# -*- coding: utf-8 -*-
2+
from comparch import Interface
3+
from abc import abstractmethod
4+
5+
class IConsumer(Interface):
6+
"""A consumer consumes steps in a stack to find an object.
7+
"""
8+
9+
@abstractmethod
10+
def __call__(self, obj, stack):
11+
"""Returns a boolean meaning that some stack has been consumed,
12+
an object and the rest of unconsumed stack
13+
"""
14+
15+
class IResource(Interface):
16+
pass
17+
18+
class ResolveError(Exception):
19+
pass
20+
21+
class ModelError(ResolveError):
22+
"""Exception raised when a model cannot be resolved
23+
"""
24+
25+
class ResourceError(ResolveError):
26+
"""Exception raised when a resource cannot be resolved
27+
"""

morepath/pathstack.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# -*- coding: utf-8 -*-
2+
import re
3+
4+
DEFAULT = u'default'
5+
RESOURCE = u'resource'
6+
7+
def parse_path(path, shortcuts=None):
8+
"""Parses a path /foo/bar/baz to a stack of steps.
9+
10+
A step is a ns, name tuple.
11+
12+
Namespaces can be indicated with ++foo++ at the beginning of a step,
13+
where 'foo' is the namespace.
14+
15+
If this is left out, the namespace is considered to be DEFAULT.
16+
17+
A dictionary of shortcuts can be supplied, where each key is a
18+
a character combination (such as '@@') that should be expanded,
19+
and the value is the namespace it should be expanded to (such as 'view').
20+
Shortcuts only exist for namespaces and are applied to the beginning
21+
of the path.
22+
"""
23+
if shortcuts is None:
24+
shortcuts = {}
25+
26+
if path == '/' or not path:
27+
return []
28+
29+
if path.startswith('/'):
30+
path = path[1:]
31+
32+
steps = re.split(r'/+', path.rstrip('/'))
33+
34+
result = []
35+
for step in steps:
36+
for key, value in shortcuts.items():
37+
if step.startswith(key):
38+
step = (u'++%s++' % value) + step[len(key):]
39+
break
40+
if step.startswith(u'++'):
41+
try:
42+
ns, name = step[2:].split(u'++', 1)
43+
except ValueError:
44+
ns = DEFAULT
45+
name = step
46+
else:
47+
ns = DEFAULT
48+
name = step
49+
result.append((ns, name))
50+
result.reverse()
51+
return result
52+
53+
def create_path(stack, shortcuts=None):
54+
"""Rebuilds a path from a stack.
55+
56+
A dictionary of shortcuts can be supplied to minimize namespaces use
57+
"""
58+
if shortcuts is None:
59+
shortcuts = {}
60+
else:
61+
inversed_shortcuts = {}
62+
for key, value in shortcuts.items():
63+
# do not allow multiple shortcuts for the same namespace
64+
assert value not in inversed_shortcuts
65+
inversed_shortcuts[value] = key
66+
shortcuts = inversed_shortcuts
67+
result = []
68+
for ns, name in reversed(stack):
69+
if ns == DEFAULT:
70+
result.append(name)
71+
continue
72+
shortcut = shortcuts.get(ns)
73+
if shortcut is not None:
74+
result.append(shortcut + name)
75+
continue
76+
result.append(u'++%s++%s' % (ns, name))
77+
return '/' + u'/'.join(result)

0 commit comments

Comments
 (0)