Merge pull request #1669 from Kodiologist/speed

Some simple speed-ups
This commit is contained in:
Kodi Arfer 2018-08-18 15:37:41 -07:00 committed by GitHub
commit c92fb3c494
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 201 additions and 85 deletions

112
fastentrypoints.py Normal file
View File

@ -0,0 +1,112 @@
# noqa: D300,D400
# Copyright (c) 2016, Aaron Christianson
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
Monkey patch setuptools to write faster console_scripts with this format:
import sys
from mymodule import entry_function
sys.exit(entry_function())
This is better.
(c) 2016, Aaron Christianson
http://github.com/ninjaaron/fast-entry_points
'''
from setuptools.command import easy_install
import re
TEMPLATE = '''\
# -*- coding: utf-8 -*-
# EASY-INSTALL-ENTRY-SCRIPT: '{3}','{4}','{5}'
__requires__ = '{3}'
import re
import sys
from {0} import {1}
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
sys.exit({2}())'''
@classmethod
def get_args(cls, dist, header=None): # noqa: D205,D400
"""
Yield write_script() argument tuples for a distribution's
console_scripts and gui_scripts entry points.
"""
if header is None:
# pylint: disable=E1101
header = cls.get_header()
spec = str(dist.as_requirement())
for type_ in 'console', 'gui':
group = type_ + '_scripts'
for name, ep in dist.get_entry_map(group).items():
# ensure_safe_name
if re.search(r'[\\/]', name):
raise ValueError("Path separators not allowed in script names")
script_text = TEMPLATE.format(
ep.module_name, ep.attrs[0], '.'.join(ep.attrs),
spec, group, name)
# pylint: disable=E1101
args = cls._get_script_args(type_, name, header, script_text)
for res in args:
yield res
# pylint: disable=E1101
easy_install.ScriptWriter.get_args = get_args
def main():
import os
import re
import shutil
import sys
dests = sys.argv[1:] or ['.']
filename = re.sub('\.pyc$', '.py', __file__)
for dst in dests:
shutil.copy(filename, dst)
manifest_path = os.path.join(dst, 'MANIFEST.in')
setup_path = os.path.join(dst, 'setup.py')
# Insert the include statement to MANIFEST.in if not present
with open(manifest_path, 'a+') as manifest:
manifest.seek(0)
manifest_content = manifest.read()
if 'include fastentrypoints.py' not in manifest_content:
manifest.write(('\n' if manifest_content else '') +
'include fastentrypoints.py')
# Insert the import statement to setup.py if not present
with open(setup_path, 'a+') as setup:
setup.seek(0)
setup_content = setup.read()
if 'import fastentrypoints' not in setup_content:
setup.seek(0)
setup.truncate()
setup.write('import fastentrypoints\n' + setup_content)

View File

@ -15,8 +15,7 @@ import astor.code_gen
import hy import hy
from hy.lex import LexException, PrematureEndOfInput from hy.lex import LexException, PrematureEndOfInput, mangle
from hy.lex.parser import mangle
from hy.compiler import HyTypeError from hy.compiler import HyTypeError
from hy.importer import (hy_eval, import_buffer_to_module, from hy.importer import (hy_eval, import_buffer_to_module,
import_file_to_ast, import_file_to_hst, import_file_to_ast, import_file_to_hst,

View File

@ -11,7 +11,7 @@ from hy.model_patterns import (FORM, SYM, KEYWORD, STR, sym, brackets, whole,
from funcparserlib.parser import some, many, oneplus, maybe, NoParseError from funcparserlib.parser import some, many, oneplus, maybe, NoParseError
from hy.errors import HyCompileError, HyTypeError from hy.errors import HyCompileError, HyTypeError
from hy.lex.parser import mangle, unmangle from hy.lex import mangle, unmangle
import hy.macros import hy.macros
from hy._compat import ( from hy._compat import (

View File

@ -19,8 +19,7 @@
(import [collections :as cabc]) (import [collections :as cabc])
(import [collections.abc :as cabc])) (import [collections.abc :as cabc]))
(import [hy.models [HySymbol HyKeyword]]) (import [hy.models [HySymbol HyKeyword]])
(import [hy.lex [LexException PrematureEndOfInput tokenize]]) (import [hy.lex [LexException PrematureEndOfInput tokenize mangle unmangle]])
(import [hy.lex.parser [mangle unmangle]])
(import [hy.compiler [HyASTCompiler]]) (import [hy.compiler [HyASTCompiler]])
(import [hy.importer [hy-eval :as eval]]) (import [hy.importer [hy-eval :as eval]])

View File

@ -2,17 +2,19 @@
# This file is part of Hy, which is free software licensed under the Expat # This file is part of Hy, which is free software licensed under the Expat
# license. See the LICENSE. # license. See the LICENSE.
from rply.errors import LexingError from __future__ import unicode_literals
import re, unicodedata
from hy._compat import str_type, isidentifier, UCS4
from hy.lex.exceptions import LexException, PrematureEndOfInput # NOQA from hy.lex.exceptions import LexException, PrematureEndOfInput # NOQA
from hy.lex.lexer import lexer
from hy.lex.parser import parser
def tokenize(buf): def tokenize(buf):
""" """
Tokenize a Lisp file or string buffer into internal Hy objects. Tokenize a Lisp file or string buffer into internal Hy objects.
""" """
from hy.lex.lexer import lexer
from hy.lex.parser import parser
from rply.errors import LexingError
try: try:
return parser.parse(lexer.lex(buf)) return parser.parse(lexer.lex(buf))
except LexingError as e: except LexingError as e:
@ -23,3 +25,80 @@ def tokenize(buf):
if e.source is None: if e.source is None:
e.source = buf e.source = buf
raise raise
mangle_delim = 'X'
def mangle(s):
"""Stringify the argument and convert it to a valid Python identifier
according to Hy's mangling rules."""
def unicode_char_to_hex(uchr):
# Covert a unicode char to hex string, without prefix
return uchr.encode('unicode-escape').decode('utf-8').lstrip('\\U').lstrip('\\u').lstrip('0')
assert s
s = str_type(s)
s = s.replace("-", "_")
s2 = s.lstrip('_')
leading_underscores = '_' * (len(s) - len(s2))
s = s2
if s.endswith("?"):
s = 'is_' + s[:-1]
if not isidentifier(leading_underscores + s):
# Replace illegal characters with their Unicode character
# names, or hexadecimal if they don't have one.
s = 'hyx_' + ''.join(
c
if c != mangle_delim and isidentifier('S' + c)
# We prepend the "S" because some characters aren't
# allowed at the start of an identifier.
else '{0}{1}{0}'.format(mangle_delim,
unicodedata.name(c, '').lower().replace('-', 'H').replace(' ', '_')
or 'U{}'.format(unicode_char_to_hex(c)))
for c in unicode_to_ucs4iter(s))
s = leading_underscores + s
assert isidentifier(s)
return s
def unmangle(s):
"""Stringify the argument and try to convert it to a pretty unmangled
form. This may not round-trip, because different Hy symbol names can
mangle to the same Python identifier."""
s = str_type(s)
s2 = s.lstrip('_')
leading_underscores = len(s) - len(s2)
s = s2
if s.startswith('hyx_'):
s = re.sub('{0}(U)?([_a-z0-9H]+?){0}'.format(mangle_delim),
lambda mo:
chr(int(mo.group(2), base=16))
if mo.group(1)
else unicodedata.lookup(
mo.group(2).replace('_', ' ').replace('H', '-').upper()),
s[len('hyx_'):])
if s.startswith('is_'):
s = s[len("is_"):] + "?"
s = s.replace('_', '-')
return '-' * leading_underscores + s
def unicode_to_ucs4iter(ustr):
# Covert a unicode string to an iterable object,
# elements in the object are single USC-4 unicode characters
if UCS4:
return ustr
ucs4_list = list(ustr)
for i, u in enumerate(ucs4_list):
if 0xD7FF < ord(u) < 0xDC00:
ucs4_list[i] += ucs4_list[i + 1]
del ucs4_list[i + 1]
return ucs4_list

View File

@ -10,7 +10,7 @@ import re, unicodedata
from rply import ParserGenerator from rply import ParserGenerator
from hy._compat import str_type, isidentifier, UCS4 from hy._compat import str_type
from hy.models import (HyBytes, HyComplex, HyDict, HyExpression, HyFloat, from hy.models import (HyBytes, HyComplex, HyDict, HyExpression, HyFloat,
HyInteger, HyKeyword, HyList, HySet, HyString, HySymbol) HyInteger, HyKeyword, HyList, HySet, HyString, HySymbol)
from .lexer import lexer from .lexer import lexer
@ -19,80 +19,6 @@ from .exceptions import LexException, PrematureEndOfInput
pg = ParserGenerator([rule.name for rule in lexer.rules] + ['$end']) pg = ParserGenerator([rule.name for rule in lexer.rules] + ['$end'])
mangle_delim = 'X'
def unicode_to_ucs4iter(ustr):
# Covert a unicode string to an iterable object,
# elements in the object are single USC-4 unicode characters
if UCS4:
return ustr
ucs4_list = list(ustr)
for i, u in enumerate(ucs4_list):
if 0xD7FF < ord(u) < 0xDC00:
ucs4_list[i] += ucs4_list[i + 1]
del ucs4_list[i + 1]
return ucs4_list
def mangle(s):
"""Stringify the argument and convert it to a valid Python identifier
according to Hy's mangling rules."""
def unicode_char_to_hex(uchr):
# Covert a unicode char to hex string, without prefix
return uchr.encode('unicode-escape').decode('utf-8').lstrip('\\U').lstrip('\\u').lstrip('0')
assert s
s = str_type(s)
s = s.replace("-", "_")
s2 = s.lstrip('_')
leading_underscores = '_' * (len(s) - len(s2))
s = s2
if s.endswith("?"):
s = 'is_' + s[:-1]
if not isidentifier(leading_underscores + s):
# Replace illegal characters with their Unicode character
# names, or hexadecimal if they don't have one.
s = 'hyx_' + ''.join(
c
if c != mangle_delim and isidentifier('S' + c)
# We prepend the "S" because some characters aren't
# allowed at the start of an identifier.
else '{0}{1}{0}'.format(mangle_delim,
unicodedata.name(c, '').lower().replace('-', 'H').replace(' ', '_')
or 'U{}'.format(unicode_char_to_hex(c)))
for c in unicode_to_ucs4iter(s))
s = leading_underscores + s
assert isidentifier(s)
return s
def unmangle(s):
"""Stringify the argument and try to convert it to a pretty unmangled
form. This may not round-trip, because different Hy symbol names can
mangle to the same Python identifier."""
s = str_type(s)
s2 = s.lstrip('_')
leading_underscores = len(s) - len(s2)
s = s2
if s.startswith('hyx_'):
s = re.sub('{0}(U)?([_a-z0-9H]+?){0}'.format(mangle_delim),
lambda mo:
chr(int(mo.group(2), base=16))
if mo.group(1)
else unicodedata.lookup(
mo.group(2).replace('_', ' ').replace('H', '-').upper()),
s[len('hyx_'):])
if s.startswith('is_'):
s = s[len("is_"):] + "?"
s = s.replace('_', '-')
return '-' * leading_underscores + s
def set_boundaries(fun): def set_boundaries(fun):
@wraps(fun) @wraps(fun)

View File

@ -5,7 +5,7 @@
from hy._compat import PY3 from hy._compat import PY3
import hy.inspect import hy.inspect
from hy.models import replace_hy_obj, HyExpression, HySymbol, wrap_value from hy.models import replace_hy_obj, HyExpression, HySymbol, wrap_value
from hy.lex.parser import mangle from hy.lex import mangle
from hy._compat import str_type from hy._compat import str_type
from hy.errors import HyTypeError, HyMacroExpansionError from hy.errors import HyTypeError, HyMacroExpansionError

View File

@ -7,6 +7,7 @@ import sys, os
from setuptools import find_packages, setup from setuptools import find_packages, setup
from setuptools.command.install import install from setuptools.command.install import install
import fastentrypoints # Monkey-patches setuptools.
from get_version import __version__ from get_version import __version__