other 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- other-2.0.0.dist-info/METADATA +69 -0
- other-2.0.0.dist-info/RECORD +5 -0
- other-2.0.0.dist-info/WHEEL +5 -0
- other-2.0.0.dist-info/top_level.txt +1 -0
- other.py +456 -0
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: other
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: The Zen of Zope, by Alex Clark
|
|
5
|
+
Author-email: Alex Clark <aclark@aclark.net>
|
|
6
|
+
Project-URL: Homepage, https://github.com/aclark4life/other
|
|
7
|
+
Classifier: Framework :: Zope
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/x-rst
|
|
12
|
+
Requires-Dist: that
|
|
13
|
+
Requires-Dist: zope.component
|
|
14
|
+
|
|
15
|
+
The Zen of Zope, by Alex Clark
|
|
16
|
+
==============================
|
|
17
|
+
|
|
18
|
+
::
|
|
19
|
+
|
|
20
|
+
>>> import other
|
|
21
|
+
The Zen of Zope, by Alex Clark
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
Beautiful is an attribute of ugly.
|
|
25
|
+
Explicit is implemented by implicit.
|
|
26
|
+
Simple is provided by complex.
|
|
27
|
+
Complex is directly provided by complicated.
|
|
28
|
+
Flat only implements nested.
|
|
29
|
+
Sparse has tagged value dense.
|
|
30
|
+
Readability count is not in range.
|
|
31
|
+
Special cases could not adapt the rules.
|
|
32
|
+
Practicality implements purity.
|
|
33
|
+
Errors should never require a specification that doesn’t extend the specification of silence.
|
|
34
|
+
Unless explicit is a multi-adapter.
|
|
35
|
+
In subscribing to ambiguity, return all the objects that refuse the temptation to guess.
|
|
36
|
+
There should be none-- and preferably only zero --output from a handler.
|
|
37
|
+
Although that way may not be obvious at first unless you've registered an adapter hook
|
|
38
|
+
Now is verified by never.
|
|
39
|
+
Although never is not implemented by *right* now.
|
|
40
|
+
If the implementation is hard to explain, it queries the bad idea utility.
|
|
41
|
+
If the implementation is easy to explain, it may query the good idea utility.
|
|
42
|
+
Implicit namespace packages are one honking great idea -- let's do more of those!
|
|
43
|
+
|
|
44
|
+
Changes
|
|
45
|
+
=======
|
|
46
|
+
|
|
47
|
+
2.0.0 (2026/10/22)
|
|
48
|
+
------------------
|
|
49
|
+
|
|
50
|
+
- Drop Python 2; require Python 3.10+
|
|
51
|
+
[aclark]
|
|
52
|
+
|
|
53
|
+
- Use ``@implementer`` class decorators instead of ``implements()``
|
|
54
|
+
[aclark]
|
|
55
|
+
|
|
56
|
+
- Move packaging to ``pyproject.toml``
|
|
57
|
+
[aclark]
|
|
58
|
+
|
|
59
|
+
- Publish to PyPI with trusted publishing via GitHub Actions
|
|
60
|
+
[aclark]
|
|
61
|
+
|
|
62
|
+
- Add tests
|
|
63
|
+
[aclark]
|
|
64
|
+
|
|
65
|
+
1.0.0 (2012/12/8)
|
|
66
|
+
-----------------
|
|
67
|
+
|
|
68
|
+
- First release
|
|
69
|
+
[aclark]
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
other.py,sha256=5WhtpMIFzhijENuZHzn-LKEyJBW2JCrKnOPjdpmuBgc,10058
|
|
2
|
+
other-2.0.0.dist-info/METADATA,sha256=YGfmip4rNF6wOCh5SN6QXXsI3u5hG9_6Mq2LFyEQwVs,2048
|
|
3
|
+
other-2.0.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
4
|
+
other-2.0.0.dist-info/top_level.txt,sha256=fk-i64x6wIlznV3vxEifrWihANkggso1xrQKRSSCH4c,6
|
|
5
|
+
other-2.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
other
|
other.py
ADDED
|
@@ -0,0 +1,456 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
from zope.interface import Attribute
|
|
3
|
+
from zope.interface import Interface
|
|
4
|
+
from zope.interface import Invalid
|
|
5
|
+
from zope.interface import directlyProvides
|
|
6
|
+
from zope.interface import directlyProvidedBy
|
|
7
|
+
from zope.interface import implementedBy
|
|
8
|
+
from zope.interface import implementer
|
|
9
|
+
from zope.interface import implementer_only
|
|
10
|
+
from zope.interface import invariant
|
|
11
|
+
from zope.interface import providedBy
|
|
12
|
+
from zope.interface.adapter import AdapterRegistry
|
|
13
|
+
from zope.interface.exceptions import BrokenImplementation
|
|
14
|
+
from zope.interface.interface import adapter_hooks
|
|
15
|
+
from zope.interface.verify import verifyObject
|
|
16
|
+
|
|
17
|
+
from zope.component import provideUtility
|
|
18
|
+
from zope.component import queryUtility
|
|
19
|
+
|
|
20
|
+
TITLE = "The Zen of Zope, by Alex Clark"
|
|
21
|
+
|
|
22
|
+
# This is a crash course on the Zope Component Architecture (ZCA), based on
|
|
23
|
+
# http://docs.zope.org/zope.interface/README.html and
|
|
24
|
+
# http://docs.zope.org/zope.component/index.html, and heavily inspired by
|
|
25
|
+
# The Anti-Zen of Python, by Daniel Greenfeld and
|
|
26
|
+
# The Zen of Python, by Tim Peters.
|
|
27
|
+
# Special thanks to Guido Van Rossum for Python, and Jim Fulton for Zope.
|
|
28
|
+
# Copyright (C) 2012 Alex Clark
|
|
29
|
+
# All rights reserved
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
__gittip_url__ = "https://www.gittip.com/aclark4life/"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class Complicated:
|
|
36
|
+
"""
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class INested(Interface):
|
|
41
|
+
"""
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
@implementer(INested)
|
|
46
|
+
class Nested:
|
|
47
|
+
"""
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class ISparse(Interface):
|
|
53
|
+
"""
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class RangeError(Invalid):
|
|
58
|
+
"""
|
|
59
|
+
"""
|
|
60
|
+
def __repr__(self):
|
|
61
|
+
"""
|
|
62
|
+
"""
|
|
63
|
+
return "RangeError(%r)" % self.args
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def range_invariant(ob):
|
|
67
|
+
"""
|
|
68
|
+
"""
|
|
69
|
+
if ob.max < ob.min:
|
|
70
|
+
raise RangeError(ob)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class IRange(Interface):
|
|
74
|
+
"""
|
|
75
|
+
"""
|
|
76
|
+
min = Attribute("Lower bound")
|
|
77
|
+
max = Attribute("Upper bound")
|
|
78
|
+
invariant(range_invariant)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@implementer(IRange)
|
|
82
|
+
class Range(object):
|
|
83
|
+
"""
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
def __init__(self, min, max):
|
|
87
|
+
"""
|
|
88
|
+
"""
|
|
89
|
+
self.min, self.max = min, max
|
|
90
|
+
|
|
91
|
+
def __repr__(self):
|
|
92
|
+
"""
|
|
93
|
+
"""
|
|
94
|
+
return "Range(%s, %s)" % (self.min, self.max)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class ISpecialCases(Interface):
|
|
98
|
+
"""
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class IPurity(Interface):
|
|
103
|
+
"""
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@implementer(IPurity)
|
|
108
|
+
class Practicality(object):
|
|
109
|
+
"""
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class IErrors(Interface):
|
|
114
|
+
"""
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class ISilence(Interface):
|
|
119
|
+
"""
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class IPass(IErrors):
|
|
124
|
+
"""
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@implementer(IErrors)
|
|
129
|
+
class Errors:
|
|
130
|
+
"""
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
@implementer(ISilence)
|
|
135
|
+
class Silence:
|
|
136
|
+
"""
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class IAmbiguity(Interface):
|
|
143
|
+
"""
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class IGuess(Interface):
|
|
148
|
+
"""
|
|
149
|
+
"""
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def handler(event):
|
|
153
|
+
"""
|
|
154
|
+
"""
|
|
155
|
+
print('handler', event)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
class IFile(Interface):
|
|
159
|
+
"""
|
|
160
|
+
"""
|
|
161
|
+
body = Attribute('Contents of the file.')
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@implementer(IFile)
|
|
165
|
+
class File(object):
|
|
166
|
+
"""
|
|
167
|
+
"""
|
|
168
|
+
body = "Uniquely communicate economically sound infrastructures"
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def hook(provided, object):
|
|
172
|
+
"""
|
|
173
|
+
"""
|
|
174
|
+
adapter = registry.lookup1(providedBy(object), provided, '')
|
|
175
|
+
return adapter(object)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class ISize(Interface):
|
|
179
|
+
"""
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
def getSize():
|
|
183
|
+
"""
|
|
184
|
+
"""
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@implementer(ISize)
|
|
188
|
+
class FileSize(object):
|
|
189
|
+
"""
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
def __init__(self, context):
|
|
193
|
+
"""
|
|
194
|
+
"""
|
|
195
|
+
self.context = context
|
|
196
|
+
|
|
197
|
+
def getSize(self):
|
|
198
|
+
"""
|
|
199
|
+
"""
|
|
200
|
+
return len(self.context.body)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class INow(Interface):
|
|
204
|
+
"""
|
|
205
|
+
"""
|
|
206
|
+
x = Attribute("The X attribute")
|
|
207
|
+
y = Attribute("The Y attribute")
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class INever(INow):
|
|
211
|
+
"""
|
|
212
|
+
"""
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
@implementer(INow)
|
|
216
|
+
class Never(object):
|
|
217
|
+
"""
|
|
218
|
+
"""
|
|
219
|
+
x = 1
|
|
220
|
+
|
|
221
|
+
def __init__(self):
|
|
222
|
+
self.y = 2
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
@implementer(INever)
|
|
226
|
+
class RightNow(object):
|
|
227
|
+
x = 1
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class IImplementation(Interface):
|
|
231
|
+
"""
|
|
232
|
+
"""
|
|
233
|
+
def explain():
|
|
234
|
+
"""
|
|
235
|
+
"""
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
@implementer(IImplementation)
|
|
239
|
+
class Implementation:
|
|
240
|
+
"""
|
|
241
|
+
"""
|
|
242
|
+
|
|
243
|
+
def __init__(self, implementation="easy to explain, it may query the "
|
|
244
|
+
"good idea utility."):
|
|
245
|
+
"""
|
|
246
|
+
"""
|
|
247
|
+
self.implementation = implementation
|
|
248
|
+
|
|
249
|
+
def explain(self):
|
|
250
|
+
"""
|
|
251
|
+
"""
|
|
252
|
+
print("If the implementation is", self.implementation)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
#------------------------------------------------------------------------------
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
print("%s\n\n" % TITLE)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
# 1) Beautiful is better than ugly.
|
|
262
|
+
|
|
263
|
+
# Interfaces are objects that specify the behavior of objects that
|
|
264
|
+
# provide them, through:
|
|
265
|
+
# - Informal documentation e.g. doc strings
|
|
266
|
+
# - Attribute definitions
|
|
267
|
+
# - Invariants, which are conditions that must hold true for
|
|
268
|
+
# an object to provide an interface
|
|
269
|
+
|
|
270
|
+
class IPerson(Interface):
|
|
271
|
+
"""
|
|
272
|
+
"""
|
|
273
|
+
beautiful = Attribute("Beautiful is an attribute of person.")
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
@implementer(IPerson)
|
|
277
|
+
class Person(object):
|
|
278
|
+
"""
|
|
279
|
+
"""
|
|
280
|
+
beautiful = True
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
person = Person()
|
|
284
|
+
if hasattr(person, 'beautiful'):
|
|
285
|
+
print(IPerson['beautiful'].__doc__)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
# 2) Explicit is better than implicit.
|
|
289
|
+
class IExplicit(Interface):
|
|
290
|
+
"""
|
|
291
|
+
We say that objects provide interfaces. If an object provides an interface
|
|
292
|
+
then the interface specifies the behavior of the object. In other words,
|
|
293
|
+
interfaces specify the behavior of the objects that provide them.
|
|
294
|
+
"""
|
|
295
|
+
|
|
296
|
+
@implementer(IExplicit)
|
|
297
|
+
class Implicit(object):
|
|
298
|
+
"""
|
|
299
|
+
We normally say that classes implement interfaces. If a class implements an
|
|
300
|
+
interface, then the instances of the class provide the interface. Objects
|
|
301
|
+
provide interfaces that their classes implement [2]. (Objects can provide
|
|
302
|
+
interfaces directly, in addition to what their classes implement.)
|
|
303
|
+
"""
|
|
304
|
+
@implementer(IExplicit)
|
|
305
|
+
class Explicit:
|
|
306
|
+
|
|
307
|
+
def __init__(self, errors, silence):
|
|
308
|
+
"""
|
|
309
|
+
"""
|
|
310
|
+
self.errors, self.silence = errors, silence
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
if IExplicit.implementedBy(Implicit):
|
|
314
|
+
print("Explicit is implemented by implicit.")
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
# 3) Simple is better than complex.
|
|
318
|
+
class IComplex(Interface):
|
|
319
|
+
"""
|
|
320
|
+
"""
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
@implementer(IComplex)
|
|
324
|
+
class Complex:
|
|
325
|
+
"""
|
|
326
|
+
"""
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
simple = Complex()
|
|
330
|
+
if IComplex.providedBy(simple):
|
|
331
|
+
print("Simple is provided by complex.")
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
# 4) Complex is better than complicated.
|
|
335
|
+
complicated = Complicated()
|
|
336
|
+
directlyProvides(complicated, IComplex)
|
|
337
|
+
for interface in directlyProvidedBy(complicated).interfaces():
|
|
338
|
+
if interface.getName() == 'IComplex':
|
|
339
|
+
print("Complex is directly provided by complicated.")
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
# 5) Flat is better than nested.
|
|
343
|
+
@implementer_only(INested)
|
|
344
|
+
class Flat(Complex):
|
|
345
|
+
"""
|
|
346
|
+
"""
|
|
347
|
+
|
|
348
|
+
interfaces = [interface for interface in implementedBy(Flat).interfaces()]
|
|
349
|
+
if len(interfaces) == 1:
|
|
350
|
+
if interfaces[0].getName() == 'INested':
|
|
351
|
+
print("Flat only implements nested.")
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
# 6) Sparse is better than dense.
|
|
355
|
+
ISparse.setTaggedValue('dense', 'Sparse has tagged value dense.')
|
|
356
|
+
tags = ISparse.getTaggedValueTags()
|
|
357
|
+
if 'dense' in tags:
|
|
358
|
+
print(ISparse.getTaggedValue('dense'))
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
# 7) Readability counts.
|
|
362
|
+
try:
|
|
363
|
+
IRange.validateInvariants(Range(2, 1))
|
|
364
|
+
except RangeError:
|
|
365
|
+
print("Readability count is not in range.")
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
# 8) Special cases aren't special enough to break the rules.
|
|
369
|
+
try:
|
|
370
|
+
ISpecialCases('the rules')
|
|
371
|
+
except TypeError:
|
|
372
|
+
print("Special cases could not adapt the rules.")
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
# 9) Although practicality beats purity.
|
|
376
|
+
practicality = Practicality()
|
|
377
|
+
if IPurity(practicality) is practicality:
|
|
378
|
+
print("Practicality implements purity.")
|
|
379
|
+
|
|
380
|
+
|
|
381
|
+
# 10) Errors should never pass silently.
|
|
382
|
+
# Register an object that depends on IErrors and provides ISilence
|
|
383
|
+
registry = AdapterRegistry() # XXX Logic below is not quite right
|
|
384
|
+
registry.register([IErrors], ISilence, 'should not', 'pass')
|
|
385
|
+
if (registry.lookup([IErrors], ISilence, 'should not') == 'pass' and
|
|
386
|
+
registry.lookup([Interface], ISilence) is None):
|
|
387
|
+
print ("Errors should never require a specification that doesn’t extend "
|
|
388
|
+
"the specification of silence.")
|
|
389
|
+
|
|
390
|
+
|
|
391
|
+
# 11) Unless explicitly silenced.
|
|
392
|
+
errors = Errors()
|
|
393
|
+
silence = Silence()
|
|
394
|
+
registry.register([IErrors, ISilence], IPass, '', Explicit)
|
|
395
|
+
explicit = registry.queryMultiAdapter((errors, silence), IPass)
|
|
396
|
+
if (explicit.__class__.__name__ == "Explicit" and
|
|
397
|
+
explicit.errors is errors and explicit.silence is silence):
|
|
398
|
+
print("Unless explicit is a multi-adapter.")
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
# 12) In the face of ambiguity, refuse the temptation to guess.
|
|
402
|
+
registry.subscribe([IAmbiguity], IGuess, 'refuse the temptation to guess')
|
|
403
|
+
if 'refuse the temptation to guess' in [sub for sub in
|
|
404
|
+
registry.subscriptions([IAmbiguity], IGuess)]:
|
|
405
|
+
print ("In subscribing to ambiguity, return all the objects that refuse "
|
|
406
|
+
"the temptation to guess.")
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
# 13) There should be one-- and preferably only one --obvious way to do it.
|
|
410
|
+
registry.subscribe([IErrors], None, handler)
|
|
411
|
+
if registry.subscriptions([IErrors], None) == [handler]:
|
|
412
|
+
print ("There should be none-- and preferably only zero --output from a "
|
|
413
|
+
"handler.")
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
# 14) Although that way may not be obvious at first unless you're Dutch.
|
|
417
|
+
adapter_hooks.append(hook)
|
|
418
|
+
registry.register([IFile], ISize, '', FileSize)
|
|
419
|
+
f = File()
|
|
420
|
+
size = ISize(f)
|
|
421
|
+
if registry.lookup1(IFile, ISize, '')(f).getSize() == size.getSize():
|
|
422
|
+
print ("Although that way may not be obvious at first unless you've "
|
|
423
|
+
"registered an adapter hook.")
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
# 15) Now is better than never.
|
|
427
|
+
never = Never()
|
|
428
|
+
if verifyObject(INow, never):
|
|
429
|
+
print("Now is verified by never.")
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
# 16) Although never is often better than *right* now.
|
|
433
|
+
right_now = RightNow()
|
|
434
|
+
try:
|
|
435
|
+
verifyObject(INever, right_now)
|
|
436
|
+
except BrokenImplementation:
|
|
437
|
+
print("Although never is not implemented by *right* now.")
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
# 17) If the implementation is hard to explain, it's a bad idea.
|
|
441
|
+
bad_idea = Implementation("hard to explain, it queries the bad idea "
|
|
442
|
+
"utility.") # XXX Logic?
|
|
443
|
+
provideUtility(bad_idea, IImplementation, 'bad idea')
|
|
444
|
+
queryUtility(IImplementation, 'bad idea').explain()
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
# 18) If the implementation is easy to explain, it may be a good idea.
|
|
448
|
+
good_idea = Implementation() # XXX Logic?
|
|
449
|
+
provideUtility(good_idea, IImplementation, 'good idea')
|
|
450
|
+
queryUtility(IImplementation, 'good idea').explain()
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
# 19) Namespaces are one honking great idea -- let's do more of those!
|
|
454
|
+
# http://www.python.org/dev/peps/pep-0420/
|
|
455
|
+
print ("Implicit namespace packages are one honking great idea -- let's do "
|
|
456
|
+
"more of those!")
|