From 7b2a1c157b947150c19ba559d74b0904a5daa8f8 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Mon, 28 Dec 2015 23:32:42 -0600 Subject: [PATCH] Bare bones of a notification system --- .gitignore | 5 +++ example.py | 10 ++++++ flake8/__init__.py | 1 + flake8/_trie.py | 74 ++++++++++++++++++++++++++++++++++++++++++ flake8/notifier.py | 22 +++++++++++++ flake8/style_guide.py | 0 setup.py | 75 +++++++++++++++++++++++++++++++++++++++++++ tests/test_trie.py | 14 ++++++++ tox.ini | 8 +++++ 9 files changed, 209 insertions(+) create mode 100644 .gitignore create mode 100644 example.py create mode 100644 flake8/__init__.py create mode 100644 flake8/_trie.py create mode 100644 flake8/notifier.py create mode 100644 flake8/style_guide.py create mode 100644 setup.py create mode 100644 tests/test_trie.py create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a41da07 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.cache/* +.tox/* +*.pyc +*.sw* +*.egg-info diff --git a/example.py b/example.py new file mode 100644 index 0000000..a201782 --- /dev/null +++ b/example.py @@ -0,0 +1,10 @@ +from flake8 import _trie as trie + + +tree = trie.Trie() +for i in range(5): + tree.add('E103', 'E103-listener-{0}'.format(i)) + j = i + 1 + tree.add('E1{0}3'.format(j), 'E1{0}3-listener'.format(j)) +for i in range(10): + tree.add('W1{0:02d}'.format(i), 'W1{0:02d}-listener'.format(i)) diff --git a/flake8/__init__.py b/flake8/__init__.py new file mode 100644 index 0000000..6e648d3 --- /dev/null +++ b/flake8/__init__.py @@ -0,0 +1 @@ +__version__ = '3.0.0a1' diff --git a/flake8/_trie.py b/flake8/_trie.py new file mode 100644 index 0000000..ddd3c19 --- /dev/null +++ b/flake8/_trie.py @@ -0,0 +1,74 @@ +"""Independent implementation of a Trie tree.""" + +__all__ = ('Trie', 'TrieNode') + + +def _iterate_stringlike_objects(string): + for i in range(len(string)): + yield string[i:i+1] + + +class Trie(object): + """The object that manages the trie nodes.""" + + def __init__(self): + """Initialize an empty trie.""" + self.root = TrieNode(None, None) + + def add(self, path, node_data): + """Add the node data to the path described.""" + node = self.root + for prefix in _iterate_stringlike_objects(path): + child = node.find_prefix(prefix) + if child is None: + child = node.add_child(prefix, []) + node = child + node.data.append(node_data) + + def find(self, path): + """Find a node based on the path provided.""" + node = self.root + for prefix in _iterate_stringlike_objects(path): + child = node.find_prefix(prefix) + if child is None: + return None + node = child + return node + + +class TrieNode(object): + """The majority of the implementation details of a Trie.""" + + def __init__(self, prefix, data, children=None): + """Initialize a TrieNode with data and children.""" + self.children = children or {} + self.data = data + self.prefix = prefix + + def __repr__(self): + """Generate an easy to read representation of the node.""" + return 'TrieNode(prefix={0}, data={1}, children={2})'.format( + self.prefix, self.data, dict(self.children) + ) + + def find_prefix(self, prefix): + """Find the prefix in the children of this node. + + :returns: A child matching the prefix or None. + :rtype: :class:`~TrieNode` or None + """ + return self.children.get(prefix, None) + + def add_child(self, prefix, data, children=None): + """Create and add a new child node. + + :returns: The newly created node + :rtype: :class:`~TrieNode` + """ + new_node = TrieNode(prefix, data, children) + self.children[prefix] = new_node + return new_node + + def traverse(self): + """Traverse children of this node in order.""" + raise NotImplementedError() diff --git a/flake8/notifier.py b/flake8/notifier.py new file mode 100644 index 0000000..7b24b2f --- /dev/null +++ b/flake8/notifier.py @@ -0,0 +1,22 @@ +from flake8 import _trie + + +class Notifier(object): + def __init__(self): + self.listeners = _trie.Trie() + + def listeners_for(self, error_code): + node = self.listeners.find(error_code) + for listener in node.data: + yield listener + if node.children: + for child in node.traverse(): + for listener in child.data: + yield listener + + def notify(self, error_code, *args, **kwargs): + for listener in self.listeners_for(error_code): + listener.notify(*args, **kwargs) + + def register_listener(self, error_code, listener): + self.listeners.add(error_code, listener) diff --git a/flake8/style_guide.py b/flake8/style_guide.py new file mode 100644 index 0000000..e69de29 diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..fb0585e --- /dev/null +++ b/setup.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +from __future__ import with_statement +from setuptools import setup +try: + # Work around a traceback with Nose on Python 2.6 + # http://bugs.python.org/issue15881#msg170215 + __import__('multiprocessing') +except ImportError: + pass + +try: + # Use https://docs.python.org/3/library/unittest.mock.html + from unittest import mock +except ImportError: + # < Python 3.3 + mock = None + + +tests_require = ['nose'] +if mock is None: + tests_require += ['mock'] + + +def get_version(fname='flake8/__init__.py'): + with open(fname) as f: + for line in f: + if line.startswith('__version__'): + return eval(line.split('=')[-1]) + + +def get_long_description(): + descr = [] + for fname in ('README.rst', 'CHANGES.rst'): + with open(fname) as f: + descr.append(f.read()) + return '\n\n'.join(descr) + + +setup( + name="flake8", + license="MIT", + version=get_version(), + description="the modular source code checker: pep8, pyflakes and co", + # long_description=get_long_description(), + author="Tarek Ziade", + author_email="tarek@ziade.org", + maintainer="Ian Cordasco", + maintainer_email="graffatcolmingov@gmail.com", + url="https://gitlab.com/pycqa/flake8", + packages=["flake8"], + install_requires=[ + "pyflakes >= 0.8.1, < 1.1", + "pep8 >= 1.5.7, != 1.6.0, != 1.6.1, != 1.6.2", + "mccabe >= 0.2.1, < 0.4", + ], + entry_points={ + 'distutils.commands': ['flake8 = flake8.main:Flake8Command'], + 'console_scripts': ['flake8 = flake8.main:main'], + 'flake8.extension': [ + 'F = flake8._pyflakes:FlakesChecker', + ], + }, + classifiers=[ + "Environment :: Console", + "Intended Audience :: Developers", + "License :: OSI Approved :: MIT License", + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 3", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Software Development :: Quality Assurance", + ], + tests_require=tests_require, + test_suite='nose.collector', +) diff --git a/tests/test_trie.py b/tests/test_trie.py new file mode 100644 index 0000000..10a10aa --- /dev/null +++ b/tests/test_trie.py @@ -0,0 +1,14 @@ +from flake8 import _trie as trie + + +def test_build_tree(): + tree = trie.Trie() + for i in range(5): + tree.add('E103', 'E103-listener-{0}'.format(i)) + j = i + 1 + tree.add('E1{0}3'.format(j), 'E1{0}3-listener'.format(j)) + for i in range(10): + tree.add('W1{0:02d}'.format(i), 'W1{0:02d}-listener'.format(i)) + + assert tree.find('E103') is not None + assert tree.find('E200') is None diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..de47800 --- /dev/null +++ b/tox.ini @@ -0,0 +1,8 @@ +[tox] +envlist = py26,py27,py32,py33,py34,py35 + +[testenv] +deps = + pytest +commands = + py.test