pylint-bot / pylint-unofficial

UNOFFICIAL playground for pylint github migration
0 stars 0 forks source link

Installing pylint via setuptools as a dependency produces SyntaxError warnings #379

Open pylint-bot opened 9 years ago

pylint-bot commented 9 years ago

Originally reported by: BitBucket: cladmi, GitHub: @cladmi?


When pylint 1.3.1 is installed as a dependency from a setuptools packaged project, it prints annoying harmless warnings on the console.

#!python

my_project_that_depends_on_pylint $ python setup.py install

Searching for pylint
Reading https://pypi.python.org/simple/pylint/
Best match: pylint 1.3.1
Downloading https://pypi.python.org/packages/source/p/pylint/pylint-1.3.1.zip#md5=3fd88d02423e167cf130e9882075b2ac
Processing pylint-1.3.1.zip
Writing /tmp/easy_install-oLa2V6/pylint-1.3.1/setup.cfg
Running pylint-1.3.1/setup.py -q bdist_egg --dist-dir /tmp/easy_install-oLa2V6/pylint-1.3.1/egg-dist-tmp-Ai2zAP
warning: no files found matching '*.html' under directory 'doc'
zip_safe flag not set; analyzing archive contents...
pylint.epylint: module references __file__
pylint.checkers.__init__: module references __path__
pylint.reporters.__init__: module references __path__
SyntaxError: ("'return' outside function", ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_return_outside_func.py', 3, None, 'return\n'))

SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_abstract_class_instantiated_py34.py', 12, 34, 'class GoodClass(object, metaclass=abc.ABCMeta):\n'))

SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_used_before_assignment_py30.py', 10, 20, '        nonlocal cnt\n'))

Sorry: IndentationError: ('expected an indented block', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/syntax_error.py', 2, 5, "print('hop')\n"))
SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_syntax_error.py', 1, 9, 'def toto\n'))

SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_abstract_class_instantiated_py30.py', 12, 34, 'class GoodClass(object, metaclass=abc.ABCMeta):\n'))

SyntaxError: ("'return' with argument inside generator",)

SyntaxError: ("default 'except:' must be last", ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_w0705.py', 28, None, '__revision__ += 1\n'))

SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_kwoa_py30.py', 3, 15, 'def function(*, foo):\n'))

SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_bad_exception_context_py30.py', 14, 25, '    raise IndexError from 1\n'))

/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_assert_2uple.py:4: SyntaxWarning: assertion is always true, perhaps remove parentheses?
  assert (1 == 1, 2 == 2), "no error"
/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_assert_2uple.py:5: SyntaxWarning: assertion is always true, perhaps remove parentheses?
  assert (1 == 1, 2 == 2) #this should generate a warning
/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_assert_2uple.py:7: SyntaxWarning: assertion is always true, perhaps remove parentheses?
  assert (1 == 1, ), "no error"
/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_assert_2uple.py:8: SyntaxWarning: assertion is always true, perhaps remove parentheses?
  assert (1 == 1, )
/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_assert_2uple.py:9: SyntaxWarning: assertion is always true, perhaps remove parentheses?
  assert (1 == 1, 2 == 2, 3 == 5), "no error"
/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_assert_2uple.py:11: SyntaxWarning: assertion is always true, perhaps remove parentheses?
  assert (True, 'error msg') #this should generate a warning
SyntaxError: ("'continue' not properly in loop", ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_continue_not_in_loop.py', 8, None, 'continue\n'))

SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_noerror_unused_variable_py30.py', 12, 21, '        nonlocal attr\n'))

SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_unused_import_py30.py', 9, 21, 'class Meta(metaclass=abc.ABCMeta):\n'))

SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_noerror_unbalanced_tuple_unpacking_py30.py', 9, 20, '    first, second, *last = (1, 2, 3, 4)\n'))

SyntaxError: ("'yield' outside function", ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_yield_outside_func.py', 3, None, 'yield 1\n'))

SyntaxError: ('from __future__ imports must occur at the beginning of the file', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_3k_removed_stuff_py_30.py', 4, None, 'from __future__ import generators\n'))

SyntaxError: ('keyword argument repeated', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_keyword_repeat.py', 8, None, 'function_default_arg(two=5, two=7)\n'))

SyntaxError: ('unknown encoding: IBO-8859-1', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_unknown_encoding.py', 0, 0, None))

SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_exec_used_py30.py', 6, 22, "exec('a = 1', globals={})\n"))

SyntaxError: ("duplicate argument '_' in function definition",)

SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg/pylint/test/input/func_undefined_metaclass_var_py30.py', 8, 20, 'class Bad(metaclass=ABCMet):\n'))

Installed /home/harter/work/iot-lab/parts/cli-tools/pylint-1.3.1-py2.7.egg
 $  python --version
Python 2.7.3

In my project setup.py it's installed as a setup dependency by setuptools-lint, in case it may change something.

setup_requires=['setuptools-lint', ...]

Note that, when installed with pip no warnings are printed.


pylint-bot commented 9 years ago

Original comment by Erik Purins (BitBucket: epu, GitHub: @epu?):


When using a windows virtualenv with pip (python version 2.7.3, virtualenv 1.11.4, and pip version 1.5.3), I see these warnings whenever I install or upgrade pylint in that virtualenv.

pylint-bot commented 9 years ago

Original comment by Claudiu Popa (BitBucket: PCManticore, GitHub: @PCManticore):


This is solved in repo's tip. Thanks.

pylint-bot commented 9 years ago

Original comment by BitBucket: cladmi, GitHub: @cladmi?:


Sorry but it's still not fixed for me using the tip:

#!python

Searching for pylint
Best match: pylint 1.4.0
Downloading https://bitbucket.org/logilab/pylint/get/default.tar.gz#egg=pylint-1.4.0
Processing default.tar.gz
Writing /tmp/easy_install-fFB7I2/logilab-pylint-436a4878444c/setup.cfg
Running logilab-pylint-436a4878444c/setup.py -q bdist_egg --dist-dir /tmp/easy_install-fFB7I2/logilab-pylint-436a4878444c/egg-dist-tmp-5q3Zgd
warning: no files found matching '*.html' under directory 'doc'
warning: no files found matching '*.sh' under directory 'test'
zip_safe flag not set; analyzing archive contents...
pylint.epylint: module references __file__
pylint.checkers.__init__: module references __path__
pylint.reporters.__init__: module references __path__
SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/functional/raising_non_exception_py3.py', 13, 14, 'raise Exc from missing # [raising-non-exception]\n'))

SyntaxError: ("'yield' outside function", ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/functional/yield_outside_func.py', 2, None, 'yield 1  # [yield-outside-function]\n'))

SyntaxError: ('unknown encoding: IBO-8859-1', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/functional/unknown_encoding_py30.py', 0, 0, None))

SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/functional/undefined_variable_py30.py', 8, 19, '    def test(self)->Undefined: # [undefined-variable]\n'))

SyntaxError: ('unknown encoding: IBO-8859-1', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/functional/unknown_encoding_pypy.py', 0, 0, None))

SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/functional/class_members_py30.py', 34, 38, 'class TestMetaclass(object, metaclass=ABCMeta):\n'))

SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/functional/abstract_method_py3.py', 36, 34, 'class Structure(object, metaclass=abc.ABCMeta):\n'))

SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/functional/unbalanced_tuple_unpacking_py30.py', 9, 20, '    first, second, *last = (1, 2, 3, 4)\n'))

SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/functional/abstract_class_instantiated_py3.py', 15, 34, 'class GoodClass(object, metaclass=abc.ABCMeta):\n'))

SyntaxError: ('unknown encoding: IBO-8859-1', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/functional/unknown_encoding_py29.py', 0, 0, None))

SyntaxError: ("'return' outside function", ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/input/func_return_outside_func.py', 3, None, 'return\n'))

SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/input/func_used_before_assignment_py30.py', 10, 20, '        nonlocal cnt\n'))

Sorry: IndentationError: ('expected an indented block', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/input/syntax_error.py', 2, 5, "print('hop')\n"))
SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/input/func_syntax_error.py', 1, 9, 'def toto\n'))

SyntaxError: ("'return' with argument inside generator",)

SyntaxError: ("default 'except:' must be last", ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/input/func_w0705.py', 28, None, '__revision__ += 1\n'))

SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/input/func_kwoa_py30.py', 3, 15, 'def function(*, foo):\n'))

SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/input/func_bad_exception_context_py30.py', 14, 25, '    raise IndexError from 1\n'))

/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/input/func_assert_2uple.py:4: SyntaxWarning: assertion is always true, perhaps remove parentheses?
  assert (1 == 1, 2 == 2), "no error"
/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/input/func_assert_2uple.py:5: SyntaxWarning: assertion is always true, perhaps remove parentheses?
  assert (1 == 1, 2 == 2) #this should generate a warning
/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/input/func_assert_2uple.py:7: SyntaxWarning: assertion is always true, perhaps remove parentheses?
  assert (1 == 1, ), "no error"
/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/input/func_assert_2uple.py:8: SyntaxWarning: assertion is always true, perhaps remove parentheses?
  assert (1 == 1, )
/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/input/func_assert_2uple.py:9: SyntaxWarning: assertion is always true, perhaps remove parentheses?
  assert (1 == 1, 2 == 2, 3 == 5), "no error"
/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/input/func_assert_2uple.py:11: SyntaxWarning: assertion is always true, perhaps remove parentheses?
  assert (True, 'error msg') #this should generate a warning
SyntaxError: ("'continue' not properly in loop", ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/input/func_continue_not_in_loop.py', 8, None, 'continue\n'))

SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/input/func_noerror_unused_variable_py30.py', 12, 21, '        nonlocal attr\n'))

SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/input/func_unused_import_py30.py', 10, 21, 'class Meta(metaclass=abc.ABCMeta):\n'))

SyntaxError: ('from __future__ imports must occur at the beginning of the file', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/input/func_3k_removed_stuff_py_30.py', 4, None, 'from __future__ import generators, print_function\n'))

SyntaxError: ('keyword argument repeated', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/input/func_keyword_repeat.py', 8, None, 'function_default_arg(two=5, two=7)\n'))

SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/input/func_exec_used_py30.py', 6, 22, "exec('a = 1', globals={})\n"))

SyntaxError: ("duplicate argument '_' in function definition",)

SyntaxError: ('invalid syntax', ('/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py2.7.egg/pylint/test/input/func_undefined_metaclass_var_py30.py', 8, 20, 'class Bad(metaclass=ABCMet):\n'))
pylint-bot commented 9 years ago

Original comment by BitBucket: cladmi, GitHub: @cladmi?:


There are also other warnings when using python3.4:

#!python

Searching for pylint
Best match: pylint 1.4.0
Downloading https://bitbucket.org/logilab/pylint/get/default.tar.gz#egg=pylint-1.4.0
Processing default.tar.gz
Writing /tmp/easy_install-2_s8t13y/logilab-pylint-436a4878444c/setup.cfg
Running logilab-pylint-436a4878444c/setup.py -q bdist_egg --dist-dir /tmp/easy_install-2_s8t13y/logilab-pylint-436a4878444c/egg-dist-tmp-sblnyati
warning: no files found matching '*.html' under directory 'doc'
warning: no files found matching '*.sh' under directory 'test'
('running 2to3 on', 'build/bdist.linux-x86_64/egg/pylint/test')
Can't parse build/bdist.linux-x86_64/egg/pylint/test/input/func_exec_used_py30.py: ParseError: bad input: type=22, value='=', context=('', (6, 21))
Can't parse build/bdist.linux-x86_64/egg/pylint/test/input/func_syntax_error.py: ParseError: bad input: type=4, value='\n', context=('', (1, 8))
Can't parse build/bdist.linux-x86_64/egg/pylint/test/input/syntax_error.py: ParseError: bad input: type=1, value='print', context=('', (2, 0))
zip_safe flag not set; analyzing archive contents...
pylint.checkers.__pycache__.__init__.cpython-34: module references __path__
pylint.reporters.__pycache__.__init__.cpython-34: module references __path__
pylint.__pycache__.epylint.cpython-34: module references __file__
  File "/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py3.4.egg/pylint/test/functional/yield_outside_func.py", line 2
    yield 1  # [yield-outside-function]
                                      ^
SyntaxError: 'yield' outside function

  File "/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py3.4.egg/pylint/test/input/func_return_outside_func.py", line 3
    return
         ^
SyntaxError: 'return' outside function

Sorry: IndentationError: expected an indented block (syntax_error.py, line 2)
  File "/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py3.4.egg/pylint/test/input/func_syntax_error.py", line 1
    def toto
           ^
SyntaxError: invalid syntax

  File "/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py3.4.egg/pylint/test/input/func_e0001_py30.py", line 12
    raise 'exception', 'message'
                     ^
SyntaxError: invalid syntax

  File "/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py3.4.egg/pylint/test/input/func_w0705.py", line 28
    __revision__ += 1
                    ^
SyntaxError: default 'except:' must be last

/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py3.4.egg/pylint/test/input/func_assert_2uple.py:4: SyntaxWarning: assertion is always true, perhaps remove parentheses?
  assert (1 == 1, 2 == 2), "no error"
/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py3.4.egg/pylint/test/input/func_assert_2uple.py:5: SyntaxWarning: assertion is always true, perhaps remove parentheses?
  assert (1 == 1, 2 == 2) #this should generate a warning
/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py3.4.egg/pylint/test/input/func_assert_2uple.py:7: SyntaxWarning: assertion is always true, perhaps remove parentheses?
  assert (1 == 1, ), "no error"
/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py3.4.egg/pylint/test/input/func_assert_2uple.py:8: SyntaxWarning: assertion is always true, perhaps remove parentheses?
  assert (1 == 1, )
/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py3.4.egg/pylint/test/input/func_assert_2uple.py:9: SyntaxWarning: assertion is always true, perhaps remove parentheses?
  assert (1 == 1, 2 == 2, 3 == 5), "no error"
/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py3.4.egg/pylint/test/input/func_assert_2uple.py:11: SyntaxWarning: assertion is always true, perhaps remove parentheses?
  assert (True, 'error msg') #this should generate a warning
  File "/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py3.4.egg/pylint/test/input/func_continue_not_in_loop.py", line 8
    continue
           ^
SyntaxError: 'continue' not properly in loop

  File "/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py3.4.egg/pylint/test/input/func_keyword_repeat.py", line 8
    function_default_arg(two=5, two=7)
                               ^
SyntaxError: keyword argument repeated

  File "/home/harter/work/iot-lab/parts/cli-tools/pylint-1.4.0-py3.4.egg/pylint/test/input/func_e0108.py", line 4
    def foo1(_, _):
SyntaxError: duplicate argument '_' in function definition
pylint-bot commented 9 years ago

Original comment by Claudiu Popa (BitBucket: PCManticore, GitHub: @PCManticore):


Hm, what command did you ran?

pylint-bot commented 9 years ago

Original comment by BitBucket: cladmi, GitHub: @cladmi?:


I'm installing/testing my own project that depends on pylint.

I'm running python setup.py install from this git repository: https://github.com/iot-lab/cli-tools

Pylint dependency is pulled by the 'setup_require' option as a dependency of setuptools-lint.

To test the last version I added this parameter to the setup function:

pylint-bot commented 9 years ago

Original comment by Claudiu Popa (BitBucket: PCManticore, GitHub: @PCManticore):


Unfortunately, we can't do anything at that point. We are stubbing byte_compile for a couple of setuptools commands, so that test files aren't compiled anymore. But that works only if the step with setup.py for pylint is the final one. This is not the case for your situation, after setuptools_lint calls bdist_egg with our setup.py, the next steps are out of our scope, since the next step will be the unpacking of the egg and the compilation of test files, which is done by setuptools_lint's setup.py. In order to alleviate this, you'll need to do in setup.py something similar with what we do.

pylint-bot commented 9 years ago

Original comment by BitBucket: cladmi, GitHub: @cladmi?:


Thank you for investigating.

Just in case, wouldn't it be possible to release the test files in a separate directory as "data_files" so that no other setup.py will ever try to byte-compile it? Like if they were simple text files with a ".py" extension.

On my side, I should improve my setup.py to only install pylint when needed and not at each install as a "setup_requires" dependency.

pylint-bot commented 9 years ago

Original comment by Claudiu Popa (BitBucket: PCManticore, GitHub: @PCManticore):


Do you mean the test files to be moved outside of pylint, as a data_files?

pylint-bot commented 9 years ago

Original comment by BitBucket: cladmi, GitHub: @cladmi?:


Still inside pylint but released with 'data_files' or 'package_data' arguments of the setup function:

https://docs.python.org/2/distutils/setupscript.html#installing-additional-files

This may prevent to compile them, and still allow running the tests if adapted.

It's just an idea, not tested on my side for the moment.

pylint-bot commented 9 years ago

Original comment by Claudiu Popa (BitBucket: PCManticore, GitHub: @PCManticore):


Oh, cool, didn't know this.