feedgenerator-1.7/0000775000175000017500000000000012210427157014772 5ustar alexisalexis00000000000000feedgenerator-1.7/setup.cfg0000664000175000017500000000012712210427157016613 0ustar alexisalexis00000000000000[sdist] force-manifest = 1 [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 feedgenerator-1.7/setup.py0000664000175000017500000000340612210426776016515 0ustar alexisalexis00000000000000#! /usr/bin/python # Using setuptools rather than distutils to get the `develop` command from setuptools import setup NAME = 'feedgenerator' PACKAGES = ['feedgenerator', 'feedgenerator.django', 'feedgenerator.django.utils'] DESCRIPTION = 'Standalone version of django.utils.feedgenerator, compatible with Py3k' LONG_DESCRIPTION = open('README.rst').read() URL = "https://github.com/dmdm/feedgenerator-py3k.git" CLASSIFIERS = ['Development Status :: 3 - Alpha', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Software Development :: Libraries :: Python Modules', ] AUTHOR = 'Django Software Foundation' AUTHOR_EMAIL = 'foundation@djangoproject.com' MAINTAINER = 'Dirk Makowski' MAINTAINER_EMAIL = 'dm@parenchym.com' KEYWORDS = "feed atom rss".split(' ') VERSION = '1.7' TEST_SUITE = 'tests_feedgenerator' REQUIRES = ['pytz >= 0a', 'six'] setup( name=NAME, version=VERSION, packages=PACKAGES, test_suite=TEST_SUITE, install_requires=REQUIRES, # metadata for upload to PyPI author=AUTHOR, author_email=AUTHOR_EMAIL, maintainer=MAINTAINER, maintainer_email=MAINTAINER_EMAIL, description=DESCRIPTION, long_description=LONG_DESCRIPTION, keywords=KEYWORDS, url=URL, classifiers=CLASSIFIERS, zip_safe=False, ) feedgenerator-1.7/tests_feedgenerator/0000775000175000017500000000000012210427157021026 5ustar alexisalexis00000000000000feedgenerator-1.7/tests_feedgenerator/usage_example.py0000664000175000017500000000203412210426463024215 0ustar alexisalexis00000000000000# -*- encoding: utf-8 -*- from __future__ import unicode_literals import os import tempfile import six import feedgenerator feed = feedgenerator.Rss201rev2Feed( title="Poynter E-Media Tidbits", link="http://www.poynter.org/column.asp?id=31", description="""A group Weblog by the sharpest minds in online media/journalism/publishing. Umlauts: äöüßÄÖÜ Chinese: 老师是四十四,是不是? Finnish: Mustan kissan paksut posket. (ah, no special chars) Kärpänen sanoi kärpäselle: tuu kattoon kattoon ku kaveri tapettiin tapettiin. """, language="en", ) feed.add_item( title="Hello", link="http://www.holovaty.com/test/", description="Testing." ) if six.PY3: FN_PREFIX = 'feed_py3-' else: FN_PREFIX = 'feed_py2-' # Usage example in feedgenerator docs opens the file in text mode, not binary. # So we do this here likewise. fd, filename = tempfile.mkstemp(prefix=FN_PREFIX, suffix='.txt', text=True) try: fh = os.fdopen(fd, 'w') feed.write(fh, 'utf-8') finally: fh.close() feedgenerator-1.7/tests_feedgenerator/__init__.py0000664000175000017500000000000012210426463023124 0ustar alexisalexis00000000000000feedgenerator-1.7/tests_feedgenerator/test_feedgenerator.py0000664000175000017500000000736312210426463025261 0ustar alexisalexis00000000000000# -*- encoding: utf-8 -*- from __future__ import unicode_literals try: import unittest2 as unittest except ImportError: import unittest import six import feedgenerator FIXT_FEED = dict( title="Poynter E-Media Tidbits", link="http://www.poynter.org/column.asp?id=31", description="""A group Weblog by the sharpest minds in online media/journalism/publishing. Umlauts: äöüßÄÖÜ Chinese: 老师是四十四,是不是? Finnish: Mustan kissan paksut posket. (ah, no special chars) Kärpänen sanoi kärpäselle: tuu kattoon kattoon ku kaveri tapettiin tapettiin. """, language="en" ) FIXT_ITEM = dict( title="Hello", link="http://www.holovaty.com/test/", description="Testing." ) EXPECTED_RESULT = """ Poynter E-Media Tidbitshttp://www.poynter.org/column.asp?id=31A group Weblog by the sharpest minds in online media/journalism/publishing. Umlauts: äöüßÄÖÜ Chinese: 老师是四十四,是不是? Finnish: Mustan kissan paksut posket. (ah, no special chars) Kärpänen sanoi kärpäselle: tuu kattoon kattoon ku kaveri tapettiin tapettiin. en%DATE%Hellohttp://www.holovaty.com/test/Testing.""" ENCODING = 'utf-8' def build_expected_result(feed, expected_result, encoding): # Result's date is of course different from the date in the fixture. # So make them equal! d = feedgenerator.rfc2822_date(feed.latest_post_date()) s = expected_result.replace('%DATE%', d) if encoding: return s.encode(encoding) else: return s class TestFeedGenerator(unittest.TestCase): def setUp(self): self.maxDiff = None def test_000_types(self): ty = str if six.PY3 else unicode for k, v in FIXT_FEED.items(): self.assertEqual(type(v), ty) for k, v in FIXT_ITEM.items(): self.assertEqual(type(v), ty) self.assertEqual(type(EXPECTED_RESULT), ty) def test_001_string_results(self): #import ipdb; ipdb.set_trace() feed = feedgenerator.Rss201rev2Feed(**FIXT_FEED) feed.add_item(**FIXT_ITEM) result = feed.writeString(ENCODING) if six.PY3: # On Python 3, result of feedgenerator is a unicode string! # So do not encode our expected_result. expected_result = build_expected_result(feed, EXPECTED_RESULT, None) else: # On Python 2, result of feedgenerator is a str string! # Expected_result must therefore encoded likewise. expected_result = build_expected_result(feed, EXPECTED_RESULT, ENCODING) # The different result types of Python 2 (str=bytes) and Python 3 # (str=text=unicode) stems from a different implementation of StringIO. # As I understand it, getvalue() in Python 2 returns the type you # originally wrote into the buffer. In Python 3 getvalue() always # returns a str (=text=unicode). # See other test: test_stringio.py #print type(result), type(expected_result) self.assertEqual(type(result), type(expected_result)) self.assertEqual(result, expected_result) def OFF_test_002_file_results(self): pass # DO-IT_YOURSELF: Run usage_example with python2 and python3. # Each will create a feed file. # Compare the files, they must be equal! # XXX Argh -- No, the lastBuildDate will differ. # But this is allowed. feedgenerator-1.7/tests_feedgenerator/test_stringio.py0000664000175000017500000000672512210426463024306 0ustar alexisalexis00000000000000# -*- encoding: utf-8 -*- from __future__ import unicode_literals, print_function try: import unittest2 as unittest except ImportError: import unittest import six from six import StringIO ENCODING = 'utf-8' S0 = 'hello world, Umlauts: äöüßÄÖÜ, Chinese: 四是四,十是十,十四是十四,四十是四十,四十四隻不識字之石獅子是死的' S0_BYTES = 'fe fi foe fam'.encode(ENCODING) #print("###", StringIO, "###") class TestStringIO(unittest.TestCase): def test_001_text(self): # If we throw unicode into the StringIO buffer, we'll # get unicode out of it. # Thank god this is the same in Python 2 and 3. if six.PY3: self.assertEqual(type(S0), str) else: self.assertEqual(type(S0), unicode) buf = StringIO() print(S0, file=buf, end="") s1 = buf.getvalue() self.assertEqual(type(S0), type(s1)) self.assertEqual(S0, s1) if six.PY3: self.assertEqual(type(s1), str) else: self.assertEqual(type(s1), unicode) def test_002_bytes(self): buf = StringIO() print(S0_BYTES, file=buf, end="") s1 = buf.getvalue() if six.PY3: # In Python 3 StringIO *ALWAYS* returns str (=text=unicode) ! # Even if we originally write bytes into the buffer, the value # we get out of it has type str! # Input is bytes self.assertEqual(type(S0_BYTES), bytes) # Output is NOT bytes... self.assertNotEqual(type(S0_BYTES), type(s1)) self.assertNotEqual(type(s1), bytes) # ...but str! self.assertEqual(type(s1), str) # So the contents are not equal! self.assertNotEqual(S0_BYTES, s1) # StringIO coerced bytes into str: # b'xyz' ---> "b'xyz'" self.assertEqual(str(S0_BYTES), s1) # See, the type info is literally present in the output str! self.assertEqual("b'" + str(S0_BYTES, encoding=ENCODING) + "'", s1) # Coercion is NOT decoding! self.assertNotEqual(S0_BYTES.decode(ENCODING), s1) self.assertNotEqual(str(S0_BYTES, encoding=ENCODING), s1) # These are the same self.assertEqual(S0_BYTES.decode(ENCODING), str(S0_BYTES, encoding=ENCODING)) # Additional note: # If we do not specify an encoding when we create a StringIO # buffer, Python 3 automatically uses the locale's preferred # encoding: locale.getpreferredencoding() # Cf. http://docs.python.org/release/3.0.1/library/io.html#io.TextIOWrapper # In my case this is the same encoding as the encoding of this source file, # namely UTF-8. If on your system both encodings are different, you may # encounter other results than the above. # # In Python 3.2 the signature of StringIO() has changed. It is no more # possible to specify an encoding here. else: # In Python 2 StringIO returns the type that we originally # wrote into the buffer. # Here we see that if we write bytes into the buffer, we'll get # bytes out of it. self.assertEqual(type(S0_BYTES), str) self.assertEqual(type(S0_BYTES), type(s1)) self.assertEqual(type(s1), str) self.assertEqual(S0_BYTES, s1) feedgenerator-1.7/PKG-INFO0000664000175000017500000000300612210427157016066 0ustar alexisalexis00000000000000Metadata-Version: 1.1 Name: feedgenerator Version: 1.7 Summary: Standalone version of django.utils.feedgenerator, compatible with Py3k Home-page: https://github.com/dmdm/feedgenerator-py3k.git Author: Dirk Makowski Author-email: dm@parenchym.com License: UNKNOWN Description: feedgenerator-py3k ================== Feedgenerator-py3k is a standalone version of Django's feedgenerator. It is based on the current Django Version 1.5.dev20120824122350. The previous feedgenerator 1.2.1 is based on rather old code, and during the port to Python 3 it became obvious that (at least) the handling of unicode strings has to be refactored. Django has evolved since, so I decided to create a new standalone version which is based upon modern code. See http://parenchym.com/pymblog/ for details. Keywords: feed,atom,rss Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Topic :: Internet :: WWW/HTTP Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Software Development :: Libraries :: Python Modules feedgenerator-1.7/feedgenerator/0000775000175000017500000000000012210427157017604 5ustar alexisalexis00000000000000feedgenerator-1.7/feedgenerator/__init__.py0000664000175000017500000000141412210426463021714 0ustar alexisalexis00000000000000""" Syndication feed generation library -- used for generating RSS, etc. Sample usage: >>> import feedgenerator >>> feed = feedgenerator.Rss201rev2Feed( ... title="Poynter E-Media Tidbits", ... link="http://www.poynter.org/column.asp?id=31", ... description="A group Weblog by the sharpest minds in online media/journalism/publishing.", ... language="en", ... ) >>> feed.add_item( ... title="Hello", ... link="http://www.holovaty.com/test/", ... description="Testing." ... ) >>> with open('test.rss', 'w') as fp: ... feed.write(fp, 'utf-8') For definitions of the different versions of RSS, see: http://web.archive.org/web/20110718035220/http://diveintomark.org/archives/2004/02/04/incompatible-rss """ from .django.utils.feedgenerator import * feedgenerator-1.7/feedgenerator/django/0000775000175000017500000000000012210427157021046 5ustar alexisalexis00000000000000feedgenerator-1.7/feedgenerator/django/__init__.py0000664000175000017500000000000012210426463023144 0ustar alexisalexis00000000000000feedgenerator-1.7/feedgenerator/django/utils/0000775000175000017500000000000012210427157022206 5ustar alexisalexis00000000000000feedgenerator-1.7/feedgenerator/django/utils/six.py0000664000175000017500000002440112210426463023363 0ustar alexisalexis00000000000000"""Utilities for writing code that runs on Python 2 and 3""" import operator import sys import types __author__ = "Benjamin Peterson " __version__ = "1.1.0" # True if we are running on Python 3. PY3 = sys.version_info[0] == 3 if PY3: string_types = str, integer_types = int, class_types = type, text_type = str binary_type = bytes MAXSIZE = sys.maxsize else: string_types = basestring, integer_types = (int, long) class_types = (type, types.ClassType) text_type = unicode binary_type = str # It's possible to have sizeof(long) != sizeof(Py_ssize_t). class X(object): def __len__(self): return 1 << 31 try: len(X()) except OverflowError: # 32-bit MAXSIZE = int((1 << 31) - 1) else: # 64-bit MAXSIZE = int((1 << 63) - 1) del X def _add_doc(func, doc): """Add documentation to a function.""" func.__doc__ = doc def _import_module(name): """Import module, returning the module after the last dot.""" __import__(name) return sys.modules[name] class _LazyDescr(object): def __init__(self, name): self.name = name def __get__(self, obj, tp): result = self._resolve() setattr(obj, self.name, result) # This is a bit ugly, but it avoids running this again. delattr(tp, self.name) return result class MovedModule(_LazyDescr): def __init__(self, name, old, new=None): super(MovedModule, self).__init__(name) if PY3: if new is None: new = name self.mod = new else: self.mod = old def _resolve(self): return _import_module(self.mod) class MovedAttribute(_LazyDescr): def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None): super(MovedAttribute, self).__init__(name) if PY3: if new_mod is None: new_mod = name self.mod = new_mod if new_attr is None: if old_attr is None: new_attr = name else: new_attr = old_attr self.attr = new_attr else: self.mod = old_mod if old_attr is None: old_attr = name self.attr = old_attr def _resolve(self): module = _import_module(self.mod) return getattr(module, self.attr) class _MovedItems(types.ModuleType): """Lazy loading of moved objects""" _moved_attributes = [ MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"), MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"), MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"), MovedAttribute("map", "itertools", "builtins", "imap", "map"), MovedAttribute("reload_module", "__builtin__", "imp", "reload"), MovedAttribute("reduce", "__builtin__", "functools"), MovedAttribute("StringIO", "StringIO", "io"), MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"), MovedAttribute("zip", "itertools", "builtins", "izip", "zip"), MovedModule("builtins", "__builtin__"), MovedModule("configparser", "ConfigParser"), MovedModule("copyreg", "copy_reg"), MovedModule("http_cookiejar", "cookielib", "http.cookiejar"), MovedModule("http_cookies", "Cookie", "http.cookies"), MovedModule("html_entities", "htmlentitydefs", "html.entities"), MovedModule("html_parser", "HTMLParser", "html.parser"), MovedModule("http_client", "httplib", "http.client"), MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"), MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"), MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"), MovedModule("cPickle", "cPickle", "pickle"), MovedModule("queue", "Queue"), MovedModule("reprlib", "repr"), MovedModule("socketserver", "SocketServer"), MovedModule("tkinter", "Tkinter"), MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"), MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"), MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"), MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"), MovedModule("tkinter_tix", "Tix", "tkinter.tix"), MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"), MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"), MovedModule("tkinter_colorchooser", "tkColorChooser", "tkinter.colorchooser"), MovedModule("tkinter_commondialog", "tkCommonDialog", "tkinter.commondialog"), MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"), MovedModule("tkinter_font", "tkFont", "tkinter.font"), MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"), MovedModule("tkinter_tksimpledialog", "tkSimpleDialog", "tkinter.simpledialog"), MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"), MovedModule("winreg", "_winreg"), ] for attr in _moved_attributes: setattr(_MovedItems, attr.name, attr) del attr moves = sys.modules["django.utils.six.moves"] = _MovedItems("moves") def add_move(move): """Add an item to six.moves.""" setattr(_MovedItems, move.name, move) def remove_move(name): """Remove item from six.moves.""" try: delattr(_MovedItems, name) except AttributeError: try: del moves.__dict__[name] except KeyError: raise AttributeError("no such move, %r" % (name,)) if PY3: _meth_func = "__func__" _meth_self = "__self__" _func_code = "__code__" _func_defaults = "__defaults__" _iterkeys = "keys" _itervalues = "values" _iteritems = "items" else: _meth_func = "im_func" _meth_self = "im_self" _func_code = "func_code" _func_defaults = "func_defaults" _iterkeys = "iterkeys" _itervalues = "itervalues" _iteritems = "iteritems" if PY3: def get_unbound_function(unbound): return unbound advance_iterator = next def callable(obj): return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) else: def get_unbound_function(unbound): return unbound.im_func def advance_iterator(it): return it.next() callable = callable _add_doc(get_unbound_function, """Get the function out of a possibly unbound function""") get_method_function = operator.attrgetter(_meth_func) get_method_self = operator.attrgetter(_meth_self) get_function_code = operator.attrgetter(_func_code) get_function_defaults = operator.attrgetter(_func_defaults) def iterkeys(d): """Return an iterator over the keys of a dictionary.""" return getattr(d, _iterkeys)() def itervalues(d): """Return an iterator over the values of a dictionary.""" return getattr(d, _itervalues)() def iteritems(d): """Return an iterator over the (key, value) pairs of a dictionary.""" return getattr(d, _iteritems)() if PY3: def b(s): return s.encode("latin-1") def u(s): return s if sys.version_info[1] <= 1: def int2byte(i): return bytes((i,)) else: # This is about 2x faster than the implementation above on 3.2+ int2byte = operator.methodcaller("to_bytes", 1, "big") import io StringIO = io.StringIO BytesIO = io.BytesIO else: def b(s): return s def u(s): return unicode(s, "unicode_escape") int2byte = chr import StringIO StringIO = BytesIO = StringIO.StringIO _add_doc(b, """Byte literal""") _add_doc(u, """Text literal""") if PY3: import builtins exec_ = getattr(builtins, "exec") def reraise(tp, value, tb=None): if value.__traceback__ is not tb: raise value.with_traceback(tb) raise value print_ = getattr(builtins, "print") del builtins else: def exec_(code, globs=None, locs=None): """Execute code in a namespace.""" if globs is None: frame = sys._getframe(1) globs = frame.f_globals if locs is None: locs = frame.f_locals del frame elif locs is None: locs = globs exec("""exec code in globs, locs""") exec_("""def reraise(tp, value, tb=None): raise tp, value, tb """) def print_(*args, **kwargs): """The new-style print function.""" fp = kwargs.pop("file", sys.stdout) if fp is None: return def write(data): if not isinstance(data, basestring): data = str(data) fp.write(data) want_unicode = False sep = kwargs.pop("sep", None) if sep is not None: if isinstance(sep, unicode): want_unicode = True elif not isinstance(sep, str): raise TypeError("sep must be None or a string") end = kwargs.pop("end", None) if end is not None: if isinstance(end, unicode): want_unicode = True elif not isinstance(end, str): raise TypeError("end must be None or a string") if kwargs: raise TypeError("invalid keyword arguments to print()") if not want_unicode: for arg in args: if isinstance(arg, unicode): want_unicode = True break if want_unicode: newline = unicode("\n") space = unicode(" ") else: newline = "\n" space = " " if sep is None: sep = space if end is None: end = newline for i, arg in enumerate(args): if i: write(sep) write(arg) write(end) _add_doc(reraise, """Reraise an exception.""") def with_metaclass(meta, base=object): """Create a base class with a metaclass.""" return meta("NewBase", (base,), {}) ### Additional customizations for Django ### if PY3: _iterlists = "lists" else: _iterlists = "iterlists" def iterlists(d): """Return an iterator over the values of a MultiValueDict.""" return getattr(d, _iterlists)() add_move(MovedModule("_dummy_thread", "dummy_thread")) add_move(MovedModule("_thread", "thread")) feedgenerator-1.7/feedgenerator/django/utils/encoding.py0000664000175000017500000002320312210426463024345 0ustar alexisalexis00000000000000from __future__ import unicode_literals import codecs import datetime from decimal import Decimal import locale try: from urllib.parse import quote except ImportError: # Python 2 from urllib import quote import warnings from .functional import Promise from . import six class DjangoUnicodeDecodeError(UnicodeDecodeError): def __init__(self, obj, *args): self.obj = obj UnicodeDecodeError.__init__(self, *args) def __str__(self): original = UnicodeDecodeError.__str__(self) return '%s. You passed in %r (%s)' % (original, self.obj, type(self.obj)) class StrAndUnicode(object): """ A class that derives __str__ from __unicode__. On Python 2, __str__ returns the output of __unicode__ encoded as a UTF-8 bytestring. On Python 3, __str__ returns the output of __unicode__. Useful as a mix-in. If you support Python 2 and 3 with a single code base, you can inherit this mix-in and just define __unicode__. """ def __init__(self, *args, **kwargs): warnings.warn("StrAndUnicode is deprecated. Define a __str__ method " "and apply the @python_2_unicode_compatible decorator " "instead.", PendingDeprecationWarning, stacklevel=2) super(StrAndUnicode, self).__init__(*args, **kwargs) if six.PY3: def __str__(self): return self.__unicode__() else: def __str__(self): return self.__unicode__().encode('utf-8') def python_2_unicode_compatible(klass): """ A decorator that defines __unicode__ and __str__ methods under Python 2. Under Python 3 it does nothing. To support Python 2 and 3 with a single code base, define a __str__ method returning text and apply this decorator to the class. """ if not six.PY3: klass.__unicode__ = klass.__str__ klass.__str__ = lambda self: self.__unicode__().encode('utf-8') return klass def smart_text(s, encoding='utf-8', strings_only=False, errors='strict'): """ Returns a text object representing 's' -- unicode on Python 2 and str on Python 3. Treats bytestrings using the 'encoding' codec. If strings_only is True, don't convert (some) non-string-like objects. """ if isinstance(s, Promise): # The input is the result of a gettext_lazy() call. return s return force_text(s, encoding, strings_only, errors) def is_protected_type(obj): """Determine if the object instance is of a protected type. Objects of protected types are preserved as-is when passed to force_text(strings_only=True). """ return isinstance(obj, six.integer_types + (type(None), float, Decimal, datetime.datetime, datetime.date, datetime.time)) def force_text(s, encoding='utf-8', strings_only=False, errors='strict'): """ Similar to smart_text, except that lazy instances are resolved to strings, rather than kept as lazy objects. If strings_only is True, don't convert (some) non-string-like objects. """ # Handle the common case first, saves 30-40% when s is an instance of # six.text_type. This function gets called often in that setting. if isinstance(s, six.text_type): return s if strings_only and is_protected_type(s): return s try: if not isinstance(s, six.string_types): if hasattr(s, '__unicode__'): s = s.__unicode__() else: try: if six.PY3: if isinstance(s, bytes): s = six.text_type(s, encoding, errors) else: s = six.text_type(s) else: s = six.text_type(bytes(s), encoding, errors) except UnicodeEncodeError: if not isinstance(s, Exception): raise # If we get to here, the caller has passed in an Exception # subclass populated with non-ASCII data without special # handling to display as a string. We need to handle this # without raising a further exception. We do an # approximation to what the Exception's standard str() # output should be. s = ' '.join([force_text(arg, encoding, strings_only, errors) for arg in s]) else: # Note: We use .decode() here, instead of six.text_type(s, encoding, # errors), so that if s is a SafeBytes, it ends up being a # SafeText at the end. s = s.decode(encoding, errors) except UnicodeDecodeError as e: if not isinstance(s, Exception): raise DjangoUnicodeDecodeError(s, *e.args) else: # If we get to here, the caller has passed in an Exception # subclass populated with non-ASCII bytestring data without a # working unicode method. Try to handle this without raising a # further exception by individually forcing the exception args # to unicode. s = ' '.join([force_text(arg, encoding, strings_only, errors) for arg in s]) return s def smart_bytes(s, encoding='utf-8', strings_only=False, errors='strict'): """ Returns a bytestring version of 's', encoded as specified in 'encoding'. If strings_only is True, don't convert (some) non-string-like objects. """ if isinstance(s, Promise): # The input is the result of a gettext_lazy() call. return s return force_bytes(s, encoding, strings_only, errors) def force_bytes(s, encoding='utf-8', strings_only=False, errors='strict'): """ Similar to smart_bytes, except that lazy instances are resolved to strings, rather than kept as lazy objects. If strings_only is True, don't convert (some) non-string-like objects. """ if isinstance(s, bytes): if encoding == 'utf-8': return s else: return s.decode('utf-8', errors).encode(encoding, errors) if strings_only and (s is None or isinstance(s, int)): return s if isinstance(s, Promise): return six.text_type(s).encode(encoding, errors) if not isinstance(s, six.string_types): try: if six.PY3: return six.text_type(s).encode(encoding) else: return bytes(s) except UnicodeEncodeError: if isinstance(s, Exception): # An Exception subclass containing non-ASCII data that doesn't # know how to print itself properly. We shouldn't raise a # further exception. return ' '.join([smart_bytes(arg, encoding, strings_only, errors) for arg in s]) return six.text_type(s).encode(encoding, errors) else: return s.encode(encoding, errors) if six.PY3: smart_str = smart_text force_str = force_text else: smart_str = smart_bytes force_str = force_bytes # backwards compatibility for Python 2 smart_unicode = smart_text force_unicode = force_text smart_str.__doc__ = """\ Apply smart_text in Python 3 and smart_bytes in Python 2. This is suitable for writing to sys.stdout (for instance). """ force_str.__doc__ = """\ Apply force_text in Python 3 and force_bytes in Python 2. """ def iri_to_uri(iri): """ Convert an Internationalized Resource Identifier (IRI) portion to a URI portion that is suitable for inclusion in a URL. This is the algorithm from section 3.1 of RFC 3987. However, since we are assuming input is either UTF-8 or unicode already, we can simplify things a little from the full method. Returns an ASCII string containing the encoded result. """ # The list of safe characters here is constructed from the "reserved" and # "unreserved" characters specified in sections 2.2 and 2.3 of RFC 3986: # reserved = gen-delims / sub-delims # gen-delims = ":" / "/" / "?" / "#" / "[" / "]" / "@" # sub-delims = "!" / "$" / "&" / "'" / "(" / ")" # / "*" / "+" / "," / ";" / "=" # unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" # Of the unreserved characters, urllib.quote already considers all but # the ~ safe. # The % character is also added to the list of safe characters here, as the # end of section 3.1 of RFC 3987 specifically mentions that % must not be # converted. if iri is None: return iri return quote(smart_bytes(iri), safe=b"/#%[]=:;$&()+,!?*@'~") def filepath_to_uri(path): """Convert an file system path to a URI portion that is suitable for inclusion in a URL. We are assuming input is either UTF-8 or unicode already. This method will encode certain chars that would normally be recognized as special chars for URIs. Note that this method does not encode the ' character, as it is a valid character within URIs. See encodeURIComponent() JavaScript function for more details. Returns an ASCII string containing the encoded result. """ if path is None: return path # I know about `os.sep` and `os.altsep` but I want to leave # some flexibility for hardcoding separators. return quote(smart_bytes(path.replace("\\", "/")), safe=b"/~!*()'") # The encoding of the default system locale but falls back to the # given fallback encoding if the encoding is unsupported by python or could # not be determined. See tickets #10335 and #5846 try: DEFAULT_LOCALE_ENCODING = locale.getdefaultlocale()[1] or 'ascii' codecs.lookup(DEFAULT_LOCALE_ENCODING) except: DEFAULT_LOCALE_ENCODING = 'ascii' feedgenerator-1.7/feedgenerator/django/utils/functional.py0000664000175000017500000003267712210426463024740 0ustar alexisalexis00000000000000import copy import operator from functools import wraps, update_wrapper import sys from . import six # You can't trivially replace this `functools.partial` because this binds to # classes and returns bound instances, whereas functools.partial (on CPython) # is a type and its instances don't bind. def curry(_curried_func, *args, **kwargs): def _curried(*moreargs, **morekwargs): return _curried_func(*(args+moreargs), **dict(kwargs, **morekwargs)) return _curried def memoize(func, cache, num_args): """ Wrap a function so that results for any argument tuple are stored in 'cache'. Note that the args to the function must be usable as dictionary keys. Only the first num_args are considered when creating the key. """ @wraps(func) def wrapper(*args): mem_args = args[:num_args] if mem_args in cache: return cache[mem_args] result = func(*args) cache[mem_args] = result return result return wrapper class cached_property(object): """ Decorator that creates converts a method with a single self argument into a property cached on the instance. """ def __init__(self, func): self.func = func def __get__(self, instance, type): res = instance.__dict__[self.func.__name__] = self.func(instance) return res class Promise(object): """ This is just a base class for the proxy class created in the closure of the lazy function. It can be used to recognize promises in code. """ pass def lazy(func, *resultclasses): """ Turns any callable into a lazy evaluated callable. You need to give result classes or types -- at least one is needed so that the automatic forcing of the lazy evaluation code is triggered. Results are not memoized; the function is evaluated on every access. """ @total_ordering class __proxy__(Promise): """ Encapsulate a function call and act as a proxy for methods that are called on the result of that function. The function is not evaluated until one of the methods on the result is called. """ __dispatch = None def __init__(self, args, kw): self.__args = args self.__kw = kw if self.__dispatch is None: self.__prepare_class__() def __reduce__(self): return ( _lazy_proxy_unpickle, (func, self.__args, self.__kw) + resultclasses ) def __prepare_class__(cls): cls.__dispatch = {} for resultclass in resultclasses: cls.__dispatch[resultclass] = {} for type_ in reversed(resultclass.mro()): for (k, v) in type_.__dict__.items(): # All __promise__ return the same wrapper method, but they # also do setup, inserting the method into the dispatch # dict. meth = cls.__promise__(resultclass, k, v) if hasattr(cls, k): continue setattr(cls, k, meth) cls._delegate_bytes = bytes in resultclasses cls._delegate_text = six.text_type in resultclasses assert not (cls._delegate_bytes and cls._delegate_text), "Cannot call lazy() with both bytes and text return types." if cls._delegate_text: if six.PY3: cls.__str__ = cls.__text_cast else: cls.__unicode__ = cls.__text_cast elif cls._delegate_bytes: if six.PY3: cls.__bytes__ = cls.__bytes_cast else: cls.__str__ = cls.__bytes_cast __prepare_class__ = classmethod(__prepare_class__) def __promise__(cls, klass, funcname, method): # Builds a wrapper around some magic method and registers that magic # method for the given type and method name. def __wrapper__(self, *args, **kw): # Automatically triggers the evaluation of a lazy value and # applies the given magic method of the result type. res = func(*self.__args, **self.__kw) for t in type(res).mro(): if t in self.__dispatch: return self.__dispatch[t][funcname](res, *args, **kw) raise TypeError("Lazy object returned unexpected type.") if klass not in cls.__dispatch: cls.__dispatch[klass] = {} cls.__dispatch[klass][funcname] = method return __wrapper__ __promise__ = classmethod(__promise__) def __text_cast(self): return func(*self.__args, **self.__kw) def __bytes_cast(self): return bytes(func(*self.__args, **self.__kw)) def __cast(self): if self._delegate_bytes: return self.__bytes_cast() elif self._delegate_text: return self.__text_cast() else: return func(*self.__args, **self.__kw) def __eq__(self, other): if isinstance(other, Promise): other = other.__cast() return self.__cast() == other def __lt__(self, other): if isinstance(other, Promise): other = other.__cast() return self.__cast() < other __hash__ = object.__hash__ def __mod__(self, rhs): if self._delegate_bytes and not six.PY3: return bytes(self) % rhs elif self._delegate_text: return six.text_type(self) % rhs else: raise AssertionError('__mod__ not supported for non-string types') def __deepcopy__(self, memo): # Instances of this class are effectively immutable. It's just a # collection of functions. So we don't need to do anything # complicated for copying. memo[id(self)] = self return self @wraps(func) def __wrapper__(*args, **kw): # Creates the proxy object, instead of the actual value. return __proxy__(args, kw) return __wrapper__ def _lazy_proxy_unpickle(func, args, kwargs, *resultclasses): return lazy(func, *resultclasses)(*args, **kwargs) def allow_lazy(func, *resultclasses): """ A decorator that allows a function to be called with one or more lazy arguments. If none of the args are lazy, the function is evaluated immediately, otherwise a __proxy__ is returned that will evaluate the function when needed. """ @wraps(func) def wrapper(*args, **kwargs): for arg in list(args) + list(six.itervalues(kwargs)): if isinstance(arg, Promise): break else: return func(*args, **kwargs) return lazy(func, *resultclasses)(*args, **kwargs) return wrapper empty = object() def new_method_proxy(func): def inner(self, *args): if self._wrapped is empty: self._setup() return func(self._wrapped, *args) return inner class LazyObject(object): """ A wrapper for another class that can be used to delay instantiation of the wrapped class. By subclassing, you have the opportunity to intercept and alter the instantiation. If you don't need to do that, use SimpleLazyObject. """ def __init__(self): self._wrapped = empty __getattr__ = new_method_proxy(getattr) def __setattr__(self, name, value): if name == "_wrapped": # Assign to __dict__ to avoid infinite __setattr__ loops. self.__dict__["_wrapped"] = value else: if self._wrapped is empty: self._setup() setattr(self._wrapped, name, value) def __delattr__(self, name): if name == "_wrapped": raise TypeError("can't delete _wrapped.") if self._wrapped is empty: self._setup() delattr(self._wrapped, name) def _setup(self): """ Must be implemented by subclasses to initialise the wrapped object. """ raise NotImplementedError # introspection support: __dir__ = new_method_proxy(dir) # Workaround for http://bugs.python.org/issue12370 _super = super class SimpleLazyObject(LazyObject): """ A lazy object initialised from any function. Designed for compound objects of unknown type. For builtins or objects of known type, use django.utils.functional.lazy. """ def __init__(self, func): """ Pass in a callable that returns the object to be wrapped. If copies are made of the resulting SimpleLazyObject, which can happen in various circumstances within Django, then you must ensure that the callable can be safely run more than once and will return the same value. """ self.__dict__['_setupfunc'] = func _super(SimpleLazyObject, self).__init__() def _setup(self): self._wrapped = self._setupfunc() if six.PY3: __bytes__ = new_method_proxy(bytes) __str__ = new_method_proxy(str) else: __str__ = new_method_proxy(str) __unicode__ = new_method_proxy(unicode) def __deepcopy__(self, memo): if self._wrapped is empty: # We have to use SimpleLazyObject, not self.__class__, because the # latter is proxied. result = SimpleLazyObject(self._setupfunc) memo[id(self)] = result return result else: return copy.deepcopy(self._wrapped, memo) # Because we have messed with __class__ below, we confuse pickle as to what # class we are pickling. It also appears to stop __reduce__ from being # called. So, we define __getstate__ in a way that cooperates with the way # that pickle interprets this class. This fails when the wrapped class is a # builtin, but it is better than nothing. def __getstate__(self): if self._wrapped is empty: self._setup() return self._wrapped.__dict__ # Need to pretend to be the wrapped class, for the sake of objects that care # about this (especially in equality tests) __class__ = property(new_method_proxy(operator.attrgetter("__class__"))) __eq__ = new_method_proxy(operator.eq) __hash__ = new_method_proxy(hash) __bool__ = new_method_proxy(bool) # Python 3 __nonzero__ = __bool__ # Python 2 class lazy_property(property): """ A property that works with subclasses by wrapping the decorated functions of the base class. """ def __new__(cls, fget=None, fset=None, fdel=None, doc=None): if fget is not None: @wraps(fget) def fget(instance, instance_type=None, name=fget.__name__): return getattr(instance, name)() if fset is not None: @wraps(fset) def fset(instance, value, name=fset.__name__): return getattr(instance, name)(value) if fdel is not None: @wraps(fdel) def fdel(instance, name=fdel.__name__): return getattr(instance, name)() return property(fget, fset, fdel, doc) def partition(predicate, values): """ Splits the values into two sets, based on the return value of the function (True/False). e.g.: >>> partition(lambda x: x > 3, range(5)) [0, 1, 2, 3], [4] """ results = ([], []) for item in values: results[predicate(item)].append(item) return results if sys.version_info >= (2,7,2): from functools import total_ordering else: # For Python < 2.7.2. Python 2.6 does not have total_ordering, and # total_ordering in 2.7 versions prior to 2.7.2 is buggy. See # http://bugs.python.org/issue10042 for details. For these versions use # code borrowed from Python 2.7.3. def total_ordering(cls): """Class decorator that fills in missing ordering methods""" convert = { '__lt__': [('__gt__', lambda self, other: not (self < other or self == other)), ('__le__', lambda self, other: self < other or self == other), ('__ge__', lambda self, other: not self < other)], '__le__': [('__ge__', lambda self, other: not self <= other or self == other), ('__lt__', lambda self, other: self <= other and not self == other), ('__gt__', lambda self, other: not self <= other)], '__gt__': [('__lt__', lambda self, other: not (self > other or self == other)), ('__ge__', lambda self, other: self > other or self == other), ('__le__', lambda self, other: not self > other)], '__ge__': [('__le__', lambda self, other: (not self >= other) or self == other), ('__gt__', lambda self, other: self >= other and not self == other), ('__lt__', lambda self, other: not self >= other)] } roots = set(dir(cls)) & set(convert) if not roots: raise ValueError('must define at least one ordering operation: < > <= >=') root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__ for opname, opfunc in convert[root]: if opname not in roots: opfunc.__name__ = opname opfunc.__doc__ = getattr(int, opname).__doc__ setattr(cls, opname, opfunc) return cls feedgenerator-1.7/feedgenerator/django/utils/__init__.py0000664000175000017500000000000012210426463024304 0ustar alexisalexis00000000000000feedgenerator-1.7/feedgenerator/django/utils/datetime_safe.py0000664000175000017500000000517512210426463025361 0ustar alexisalexis00000000000000# Python's datetime strftime doesn't handle dates before 1900. # These classes override date and datetime to support the formatting of a date # through its full "proleptic Gregorian" date range. # # Based on code submitted to comp.lang.python by Andrew Dalke # # >>> datetime_safe.date(1850, 8, 2).strftime("%Y/%m/%d was a %A") # '1850/08/02 was a Friday' from datetime import date as real_date, datetime as real_datetime import re import time class date(real_date): def strftime(self, fmt): return strftime(self, fmt) class datetime(real_datetime): def strftime(self, fmt): return strftime(self, fmt) def combine(self, date, time): return datetime(date.year, date.month, date.day, time.hour, time.minute, time.microsecond, time.tzinfo) def date(self): return date(self.year, self.month, self.day) def new_date(d): "Generate a safe date from a datetime.date object." return date(d.year, d.month, d.day) def new_datetime(d): """ Generate a safe datetime from a datetime.date or datetime.datetime object. """ kw = [d.year, d.month, d.day] if isinstance(d, real_datetime): kw.extend([d.hour, d.minute, d.second, d.microsecond, d.tzinfo]) return datetime(*kw) # This library does not support strftime's "%s" or "%y" format strings. # Allowed if there's an even number of "%"s because they are escaped. _illegal_formatting = re.compile(r"((^|[^%])(%%)*%[sy])") def _findall(text, substr): # Also finds overlaps sites = [] i = 0 while 1: j = text.find(substr, i) if j == -1: break sites.append(j) i=j+1 return sites def strftime(dt, fmt): if dt.year >= 1900: return super(type(dt), dt).strftime(fmt) illegal_formatting = _illegal_formatting.search(fmt) if illegal_formatting: raise TypeError("strftime of dates before 1900 does not handle" + illegal_formatting.group(0)) year = dt.year # For every non-leap year century, advance by # 6 years to get into the 28-year repeat cycle delta = 2000 - year off = 6 * (delta // 100 + delta // 400) year = year + off # Move to around the year 2000 year = year + ((2000 - year) // 28) * 28 timetuple = dt.timetuple() s1 = time.strftime(fmt, (year,) + timetuple[1:]) sites1 = _findall(s1, str(year)) s2 = time.strftime(fmt, (year+28,) + timetuple[1:]) sites2 = _findall(s2, str(year+28)) sites = [] for site in sites1: if site in sites2: sites.append(site) s = s1 syear = "%04d" % (dt.year,) for site in sites: s = s[:site] + syear + s[site+4:] return s feedgenerator-1.7/feedgenerator/django/utils/timezone.py0000664000175000017500000002052512210426463024415 0ustar alexisalexis00000000000000"""Timezone helper functions. This module uses pytz when it's available and fallbacks when it isn't. """ from datetime import datetime, timedelta, tzinfo from threading import local import time as _time try: import pytz except ImportError: pytz = None # ### from django.conf import settings from . import six __all__ = [ 'utc', 'get_default_timezone', 'get_current_timezone', 'activate', 'deactivate', 'override', 'is_naive', 'is_aware', 'make_aware', 'make_naive', ] # UTC and local time zones ZERO = timedelta(0) class UTC(tzinfo): """ UTC implementation taken from Python's docs. Used only when pytz isn't available. """ def __repr__(self): return "" def utcoffset(self, dt): return ZERO def tzname(self, dt): return "UTC" def dst(self, dt): return ZERO class LocalTimezone(tzinfo): """ Local time implementation taken from Python's docs. Used only when pytz isn't available, and most likely inaccurate. If you're having trouble with this class, don't waste your time, just install pytz. """ def __init__(self): # This code is moved in __init__ to execute it as late as possible # See get_default_timezone(). self.STDOFFSET = timedelta(seconds=-_time.timezone) if _time.daylight: self.DSTOFFSET = timedelta(seconds=-_time.altzone) else: self.DSTOFFSET = self.STDOFFSET self.DSTDIFF = self.DSTOFFSET - self.STDOFFSET tzinfo.__init__(self) def __repr__(self): return "" def utcoffset(self, dt): if self._isdst(dt): return self.DSTOFFSET else: return self.STDOFFSET def dst(self, dt): if self._isdst(dt): return self.DSTDIFF else: return ZERO def tzname(self, dt): return _time.tzname[self._isdst(dt)] def _isdst(self, dt): tt = (dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.weekday(), 0, 0) stamp = _time.mktime(tt) tt = _time.localtime(stamp) return tt.tm_isdst > 0 utc = pytz.utc if pytz else UTC() """UTC time zone as a tzinfo instance.""" # ### # In order to avoid accessing the settings at compile time, # ### # wrap the expression in a function and cache the result. # ### _localtime = None # ### # ### def get_default_timezone(): # ### """ # ### Returns the default time zone as a tzinfo instance. # ### # ### This is the time zone defined by settings.TIME_ZONE. # ### # ### See also :func:`get_current_timezone`. # ### """ # ### global _localtime # ### if _localtime is None: # ### if isinstance(settings.TIME_ZONE, six.string_types) and pytz is not None: # ### _localtime = pytz.timezone(settings.TIME_ZONE) # ### else: # ### _localtime = LocalTimezone() # ### return _localtime # This function exists for consistency with get_current_timezone_name def get_default_timezone_name(): """ Returns the name of the default time zone. """ return _get_timezone_name(get_default_timezone()) _active = local() def get_current_timezone(): """ Returns the currently active time zone as a tzinfo instance. """ return getattr(_active, "value", get_default_timezone()) def get_current_timezone_name(): """ Returns the name of the currently active time zone. """ return _get_timezone_name(get_current_timezone()) def _get_timezone_name(timezone): """ Returns the name of ``timezone``. """ try: # for pytz timezones return timezone.zone except AttributeError: # for regular tzinfo objects local_now = datetime.now(timezone) return timezone.tzname(local_now) # Timezone selection functions. # These functions don't change os.environ['TZ'] and call time.tzset() # because it isn't thread safe. def activate(timezone): """ Sets the time zone for the current thread. The ``timezone`` argument must be an instance of a tzinfo subclass or a time zone name. If it is a time zone name, pytz is required. """ if isinstance(timezone, tzinfo): _active.value = timezone elif isinstance(timezone, six.string_types) and pytz is not None: _active.value = pytz.timezone(timezone) else: raise ValueError("Invalid timezone: %r" % timezone) def deactivate(): """ Unsets the time zone for the current thread. Django will then use the time zone defined by settings.TIME_ZONE. """ if hasattr(_active, "value"): del _active.value class override(object): """ Temporarily set the time zone for the current thread. This is a context manager that uses ``~django.utils.timezone.activate()`` to set the timezone on entry, and restores the previously active timezone on exit. The ``timezone`` argument must be an instance of a ``tzinfo`` subclass, a time zone name, or ``None``. If is it a time zone name, pytz is required. If it is ``None``, Django enables the default time zone. """ def __init__(self, timezone): self.timezone = timezone self.old_timezone = getattr(_active, 'value', None) def __enter__(self): if self.timezone is None: deactivate() else: activate(self.timezone) def __exit__(self, exc_type, exc_value, traceback): if self.old_timezone is not None: _active.value = self.old_timezone else: del _active.value # ### # Templates # ### # ### def template_localtime(value, use_tz=None): # ### """ # ### Checks if value is a datetime and converts it to local time if necessary. # ### # ### If use_tz is provided and is not None, that will force the value to # ### be converted (or not), overriding the value of settings.USE_TZ. # ### # ### This function is designed for use by the template engine. # ### """ # ### should_convert = (isinstance(value, datetime) # ### and (settings.USE_TZ if use_tz is None else use_tz) # ### and not is_naive(value) # ### and getattr(value, 'convert_to_local_time', True)) # ### return localtime(value) if should_convert else value # Utilities def localtime(value, timezone=None): """ Converts an aware datetime.datetime to local time. Local time is defined by the current time zone, unless another time zone is specified. """ if timezone is None: timezone = get_current_timezone() value = value.astimezone(timezone) if hasattr(timezone, 'normalize'): # available for pytz time zones value = timezone.normalize(value) return value # ### def now(): # ### """ # ### Returns an aware or naive datetime.datetime, depending on settings.USE_TZ. # ### """ # ### if settings.USE_TZ: # ### # timeit shows that datetime.now(tz=utc) is 24% slower # ### return datetime.utcnow().replace(tzinfo=utc) # ### else: # ### return datetime.now() # By design, these four functions don't perform any checks on their arguments. # The caller should ensure that they don't receive an invalid value like None. def is_aware(value): """ Determines if a given datetime.datetime is aware. The logic is described in Python's docs: http://docs.python.org/library/datetime.html#datetime.tzinfo """ return value.tzinfo is not None and value.tzinfo.utcoffset(value) is not None def is_naive(value): """ Determines if a given datetime.datetime is naive. The logic is described in Python's docs: http://docs.python.org/library/datetime.html#datetime.tzinfo """ return value.tzinfo is None or value.tzinfo.utcoffset(value) is None def make_aware(value, timezone): """ Makes a naive datetime.datetime in a given time zone aware. """ if hasattr(timezone, 'localize'): # available for pytz time zones return timezone.localize(value, is_dst=None) else: # may be wrong around DST changes return value.replace(tzinfo=timezone) def make_naive(value, timezone): """ Makes an aware datetime.datetime naive in a given time zone. """ value = value.astimezone(timezone) if hasattr(timezone, 'normalize'): # available for pytz time zones value = timezone.normalize(value) return value.replace(tzinfo=None) feedgenerator-1.7/feedgenerator/django/utils/feedgenerator.py0000664000175000017500000003632512210426463025402 0ustar alexisalexis00000000000000""" Syndication feed generation library -- used for generating RSS, etc. Sample usage: >>> from django.utils import feedgenerator >>> feed = feedgenerator.Rss201rev2Feed( ... title="Poynter E-Media Tidbits", ... link="http://www.poynter.org/column.asp?id=31", ... description="A group Weblog by the sharpest minds in online media/journalism/publishing.", ... language="en", ... ) >>> feed.add_item( ... title="Hello", ... link="http://www.holovaty.com/test/", ... description="Testing." ... ) >>> with open('test.rss', 'w') as fp: ... feed.write(fp, 'utf-8') For definitions of the different versions of RSS, see: http://web.archive.org/web/20110718035220/http://diveintomark.org/archives/2004/02/04/incompatible-rss """ from __future__ import unicode_literals import datetime try: from urllib.parse import urlparse except ImportError: # Python 2 from urlparse import urlparse from .xmlutils import SimplerXMLGenerator from .encoding import force_text, iri_to_uri from . import datetime_safe from . import six from .six import StringIO from .timezone import is_aware def rfc2822_date(date): # We can't use strftime() because it produces locale-dependant results, so # we have to map english month and day names manually months = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec',) days = ('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun') # Support datetime objects older than 1900 date = datetime_safe.new_datetime(date) # We do this ourselves to be timezone aware, email.Utils is not tz aware. dow = days[date.weekday()] month = months[date.month - 1] time_str = date.strftime('%s, %%d %s %%Y %%H:%%M:%%S ' % (dow, month)) if not six.PY3: # strftime returns a byte string in Python 2 time_str = time_str.decode('utf-8') if is_aware(date): offset = date.tzinfo.utcoffset(date) timezone = (offset.days * 24 * 60) + (offset.seconds // 60) hour, minute = divmod(timezone, 60) return time_str + '%+03d%02d' % (hour, minute) else: return time_str + '-0000' def rfc3339_date(date): # Support datetime objects older than 1900 date = datetime_safe.new_datetime(date) time_str = date.strftime('%Y-%m-%dT%H:%M:%S') if not six.PY3: # strftime returns a byte string in Python 2 time_str = time_str.decode('utf-8') if is_aware(date): offset = date.tzinfo.utcoffset(date) timezone = (offset.days * 24 * 60) + (offset.seconds // 60) hour, minute = divmod(timezone, 60) return time_str + '%+03d:%02d' % (hour, minute) else: return time_str + 'Z' def get_tag_uri(url, date): """ Creates a TagURI. See http://web.archive.org/web/20110514113830/http://diveintomark.org/archives/2004/05/28/howto-atom-id """ bits = urlparse(url) d = '' if date is not None: d = ',%s' % datetime_safe.new_datetime(date).strftime('%Y-%m-%d') return 'tag:%s%s:%s/%s' % (bits.hostname, d, bits.path, bits.fragment) class SyndicationFeed(object): "Base class for all syndication feeds. Subclasses should provide write()" def __init__(self, title, link, description, language=None, author_email=None, author_name=None, author_link=None, subtitle=None, categories=None, feed_url=None, feed_copyright=None, feed_guid=None, ttl=None, **kwargs): to_unicode = lambda s: force_text(s, strings_only=True) if categories: categories = [force_text(c) for c in categories] if ttl is not None: # Force ints to unicode ttl = force_text(ttl) self.feed = { 'title': to_unicode(title), 'link': iri_to_uri(link), 'description': to_unicode(description), 'language': to_unicode(language), 'author_email': to_unicode(author_email), 'author_name': to_unicode(author_name), 'author_link': iri_to_uri(author_link), 'subtitle': to_unicode(subtitle), 'categories': categories or (), 'feed_url': iri_to_uri(feed_url), 'feed_copyright': to_unicode(feed_copyright), 'id': feed_guid or link, 'ttl': ttl, } self.feed.update(kwargs) self.items = [] def add_item(self, title, link, description, author_email=None, author_name=None, author_link=None, pubdate=None, comments=None, unique_id=None, enclosure=None, categories=(), item_copyright=None, ttl=None, **kwargs): """ Adds an item to the feed. All args are expected to be Python Unicode objects except pubdate, which is a datetime.datetime object, and enclosure, which is an instance of the Enclosure class. """ to_unicode = lambda s: force_text(s, strings_only=True) if categories: categories = [to_unicode(c) for c in categories] if ttl is not None: # Force ints to unicode ttl = force_text(ttl) item = { 'title': to_unicode(title), 'link': iri_to_uri(link), 'description': to_unicode(description), 'author_email': to_unicode(author_email), 'author_name': to_unicode(author_name), 'author_link': iri_to_uri(author_link), 'pubdate': pubdate, 'comments': to_unicode(comments), 'unique_id': to_unicode(unique_id), 'enclosure': enclosure, 'categories': categories or (), 'item_copyright': to_unicode(item_copyright), 'ttl': ttl, } item.update(kwargs) self.items.append(item) def num_items(self): return len(self.items) def root_attributes(self): """ Return extra attributes to place on the root (i.e. feed/channel) element. Called from write(). """ return {} def add_root_elements(self, handler): """ Add elements in the root (i.e. feed/channel) element. Called from write(). """ pass def item_attributes(self, item): """ Return extra attributes to place on each item (i.e. item/entry) element. """ return {} def add_item_elements(self, handler, item): """ Add elements on each item (i.e. item/entry) element. """ pass def write(self, outfile, encoding): """ Outputs the feed in the given encoding to outfile, which is a file-like object. Subclasses should override this. """ raise NotImplementedError def writeString(self, encoding): """ Returns the feed in the given encoding as a string. """ s = StringIO() self.write(s, encoding) return s.getvalue() def latest_post_date(self): """ Returns the latest item's pubdate. If none of them have a pubdate, this returns the current date/time. """ updates = [i['pubdate'] for i in self.items if i['pubdate'] is not None] if len(updates) > 0: updates.sort() return updates[-1] else: return datetime.datetime.now() class Enclosure(object): "Represents an RSS enclosure" def __init__(self, url, length, mime_type): "All args are expected to be Python Unicode objects" self.length, self.mime_type = length, mime_type self.url = iri_to_uri(url) class RssFeed(SyndicationFeed): mime_type = 'application/rss+xml; charset=utf-8' def write(self, outfile, encoding): handler = SimplerXMLGenerator(outfile, encoding) handler.startDocument() handler.startElement("rss", self.rss_attributes()) handler.startElement("channel", self.root_attributes()) self.add_root_elements(handler) self.write_items(handler) self.endChannelElement(handler) handler.endElement("rss") def rss_attributes(self): return {'xmlns:atom': 'http://www.w3.org/2005/Atom', 'version': self._version} def write_items(self, handler): for item in self.items: handler.startElement('item', self.item_attributes(item)) self.add_item_elements(handler, item) handler.endElement("item") def add_root_elements(self, handler): handler.addQuickElement("title", self.feed['title']) handler.addQuickElement("link", self.feed['link']) handler.addQuickElement("description", self.feed['description']) if self.feed['feed_url'] is not None: handler.addQuickElement("atom:link", None, {"rel": "self", "href": self.feed['feed_url']}) if self.feed['language'] is not None: handler.addQuickElement("language", self.feed['language']) for cat in self.feed['categories']: handler.addQuickElement("category", cat) if self.feed['feed_copyright'] is not None: handler.addQuickElement("copyright", self.feed['feed_copyright']) handler.addQuickElement("lastBuildDate", rfc2822_date(self.latest_post_date())) if self.feed['ttl'] is not None: handler.addQuickElement("ttl", self.feed['ttl']) def endChannelElement(self, handler): handler.endElement("channel") class RssUserland091Feed(RssFeed): _version = "0.91" def add_item_elements(self, handler, item): handler.addQuickElement("title", item['title']) handler.addQuickElement("link", item['link']) if item['description'] is not None: handler.addQuickElement("description", item['description']) class Rss201rev2Feed(RssFeed): # Spec: http://blogs.law.harvard.edu/tech/rss _version = "2.0" def add_item_elements(self, handler, item): handler.addQuickElement("title", item['title']) handler.addQuickElement("link", item['link']) if item['description'] is not None: handler.addQuickElement("description", item['description']) # Author information. if item["author_name"] and item["author_email"]: handler.addQuickElement("author", "%s (%s)" % \ (item['author_email'], item['author_name'])) elif item["author_email"]: handler.addQuickElement("author", item["author_email"]) elif item["author_name"]: handler.addQuickElement("dc:creator", item["author_name"], {"xmlns:dc": "http://purl.org/dc/elements/1.1/"}) if item['pubdate'] is not None: handler.addQuickElement("pubDate", rfc2822_date(item['pubdate'])) if item['comments'] is not None: handler.addQuickElement("comments", item['comments']) if item['unique_id'] is not None: handler.addQuickElement("guid", item['unique_id']) if item['ttl'] is not None: handler.addQuickElement("ttl", item['ttl']) # Enclosure. if item['enclosure'] is not None: handler.addQuickElement("enclosure", '', {"url": item['enclosure'].url, "length": item['enclosure'].length, "type": item['enclosure'].mime_type}) # Categories. for cat in item['categories']: handler.addQuickElement("category", cat) class Atom1Feed(SyndicationFeed): # Spec: http://atompub.org/2005/07/11/draft-ietf-atompub-format-10.html mime_type = 'application/atom+xml; charset=utf-8' ns = "http://www.w3.org/2005/Atom" def write(self, outfile, encoding): handler = SimplerXMLGenerator(outfile, encoding) handler.startDocument() handler.startElement('feed', self.root_attributes()) self.add_root_elements(handler) self.write_items(handler) handler.endElement("feed") def root_attributes(self): if self.feed['language'] is not None: return {"xmlns": self.ns, "xml:lang": self.feed['language']} else: return {"xmlns": self.ns} def add_root_elements(self, handler): handler.addQuickElement("title", self.feed['title']) handler.addQuickElement("link", "", {"rel": "alternate", "href": self.feed['link']}) if self.feed['feed_url'] is not None: handler.addQuickElement("link", "", {"rel": "self", "href": self.feed['feed_url']}) handler.addQuickElement("id", self.feed['id']) handler.addQuickElement("updated", rfc3339_date(self.latest_post_date())) if self.feed['author_name'] is not None: handler.startElement("author", {}) handler.addQuickElement("name", self.feed['author_name']) if self.feed['author_email'] is not None: handler.addQuickElement("email", self.feed['author_email']) if self.feed['author_link'] is not None: handler.addQuickElement("uri", self.feed['author_link']) handler.endElement("author") if self.feed['subtitle'] is not None: handler.addQuickElement("subtitle", self.feed['subtitle']) for cat in self.feed['categories']: handler.addQuickElement("category", "", {"term": cat}) if self.feed['feed_copyright'] is not None: handler.addQuickElement("rights", self.feed['feed_copyright']) def write_items(self, handler): for item in self.items: handler.startElement("entry", self.item_attributes(item)) self.add_item_elements(handler, item) handler.endElement("entry") def add_item_elements(self, handler, item): handler.addQuickElement("title", item['title']) handler.addQuickElement("link", "", {"href": item['link'], "rel": "alternate"}) if item['pubdate'] is not None: handler.addQuickElement("updated", rfc3339_date(item['pubdate'])) # Author information. if item['author_name'] is not None: handler.startElement("author", {}) handler.addQuickElement("name", item['author_name']) if item['author_email'] is not None: handler.addQuickElement("email", item['author_email']) if item['author_link'] is not None: handler.addQuickElement("uri", item['author_link']) handler.endElement("author") # Unique ID. if item['unique_id'] is not None: unique_id = item['unique_id'] else: unique_id = get_tag_uri(item['link'], item['pubdate']) handler.addQuickElement("id", unique_id) # Summary. if item['description'] is not None: handler.addQuickElement("summary", item['description'], {"type": "html"}) # Enclosure. if item['enclosure'] is not None: handler.addQuickElement("link", '', {"rel": "enclosure", "href": item['enclosure'].url, "length": item['enclosure'].length, "type": item['enclosure'].mime_type}) # Categories. for cat in item['categories']: handler.addQuickElement("category", "", {"term": cat}) # Rights. if item['item_copyright'] is not None: handler.addQuickElement("rights", item['item_copyright']) # This isolates the decision of what the system default is, so calling code can # do "feedgenerator.DefaultFeed" instead of "feedgenerator.Rss201rev2Feed". DefaultFeed = Rss201rev2Feed feedgenerator-1.7/feedgenerator/django/utils/xmlutils.py0000664000175000017500000000133212210426463024437 0ustar alexisalexis00000000000000""" Utilities for XML generation/parsing. """ import six from xml.sax.saxutils import XMLGenerator, quoteattr class SimplerXMLGenerator(XMLGenerator): def addQuickElement(self, name, contents=None, attrs=None): "Convenience method for adding an element with no children" if attrs is None: attrs = {} self.startElement(name, attrs) if contents is not None: self.characters(contents) self.endElement(name) def startElement(self, name, attrs): self._write('<' + name) # sort attributes for consistent output for (name, value) in sorted(attrs.items()): self._write(' %s=%s' % (name, quoteattr(value))) self._write(six.u('>')) feedgenerator-1.7/MANIFEST.in0000664000175000017500000000015012210426463016523 0ustar alexisalexis00000000000000include README.rst tox.ini global-include *.py recursive-include tests * recursive-exclude tests *.pyc feedgenerator-1.7/feedgenerator.egg-info/0000775000175000017500000000000012210427157021276 5ustar alexisalexis00000000000000feedgenerator-1.7/feedgenerator.egg-info/top_level.txt0000664000175000017500000000001612210427157024025 0ustar alexisalexis00000000000000feedgenerator feedgenerator-1.7/feedgenerator.egg-info/dependency_links.txt0000664000175000017500000000000112210427157025344 0ustar alexisalexis00000000000000 feedgenerator-1.7/feedgenerator.egg-info/not-zip-safe0000664000175000017500000000000112210427120023512 0ustar alexisalexis00000000000000 feedgenerator-1.7/feedgenerator.egg-info/PKG-INFO0000664000175000017500000000300612210427157022372 0ustar alexisalexis00000000000000Metadata-Version: 1.1 Name: feedgenerator Version: 1.7 Summary: Standalone version of django.utils.feedgenerator, compatible with Py3k Home-page: https://github.com/dmdm/feedgenerator-py3k.git Author: Dirk Makowski Author-email: dm@parenchym.com License: UNKNOWN Description: feedgenerator-py3k ================== Feedgenerator-py3k is a standalone version of Django's feedgenerator. It is based on the current Django Version 1.5.dev20120824122350. The previous feedgenerator 1.2.1 is based on rather old code, and during the port to Python 3 it became obvious that (at least) the handling of unicode strings has to be refactored. Django has evolved since, so I decided to create a new standalone version which is based upon modern code. See http://parenchym.com/pymblog/ for details. Keywords: feed,atom,rss Platform: UNKNOWN Classifier: Development Status :: 3 - Alpha Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Topic :: Internet :: WWW/HTTP Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content Classifier: Topic :: Software Development :: Libraries :: Python Modules feedgenerator-1.7/feedgenerator.egg-info/requires.txt0000664000175000017500000000001612210427157023673 0ustar alexisalexis00000000000000pytz >= 0a sixfeedgenerator-1.7/feedgenerator.egg-info/SOURCES.txt0000664000175000017500000000143312210427157023163 0ustar alexisalexis00000000000000MANIFEST.in README.rst setup.cfg setup.py tox.ini feedgenerator/__init__.py feedgenerator.egg-info/PKG-INFO feedgenerator.egg-info/SOURCES.txt feedgenerator.egg-info/dependency_links.txt feedgenerator.egg-info/not-zip-safe feedgenerator.egg-info/requires.txt feedgenerator.egg-info/top_level.txt feedgenerator/django/__init__.py feedgenerator/django/utils/__init__.py feedgenerator/django/utils/datetime_safe.py feedgenerator/django/utils/encoding.py feedgenerator/django/utils/feedgenerator.py feedgenerator/django/utils/functional.py feedgenerator/django/utils/six.py feedgenerator/django/utils/timezone.py feedgenerator/django/utils/xmlutils.py tests_feedgenerator/__init__.py tests_feedgenerator/test_feedgenerator.py tests_feedgenerator/test_stringio.py tests_feedgenerator/usage_example.pyfeedgenerator-1.7/tox.ini0000664000175000017500000000033112210426463016301 0ustar alexisalexis00000000000000[tox] envlist = py26,py27,py32,py33 [testenv] commands = unit2 discover [testenv:py26] deps = unittest2 [testenv:py27] deps = unittest2 [testenv:py32] deps = unittest2py3k [testenv:py33] deps = unittest2py3k feedgenerator-1.7/README.rst0000664000175000017500000000100112210426463016450 0ustar alexisalexis00000000000000feedgenerator-py3k ================== Feedgenerator-py3k is a standalone version of Django's feedgenerator. It is based on the current Django Version 1.5.dev20120824122350. The previous feedgenerator 1.2.1 is based on rather old code, and during the port to Python 3 it became obvious that (at least) the handling of unicode strings has to be refactored. Django has evolved since, so I decided to create a new standalone version which is based upon modern code. See http://parenchym.com/pymblog/ for details.