Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-calmjs.parse for
openSUSE:Factory checked in at 2023-11-13 22:17:56
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-calmjs.parse (Old)
and /work/SRC/openSUSE:Factory/.python-calmjs.parse.new.17445 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-calmjs.parse"
Mon Nov 13 22:17:56 2023 rev:7 rq:1124864 version:1.3.1
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-calmjs.parse/python-calmjs.parse.changes
2023-06-23 21:53:39.498977225 +0200
+++
/work/SRC/openSUSE:Factory/.python-calmjs.parse.new.17445/python-calmjs.parse.changes
2023-11-13 22:20:33.450810508 +0100
@@ -1,0 +2,12 @@
+Fri Nov 10 11:43:35 UTC 2023 - Dirk Müller <[email protected]>
+
+- update to 1.3.1:
+ * Modified existing ``setup.py`` hook from an install hook to a
+ build hook to ensure the generated module files are present.
+ Should any of those modules are missing and the required
+ dependencies for are not present (i.e. ``ply`` and
+ ``setuptools``), the build will result in a
+ non-zero exit status and the documented error message should
+ reflect which of the required dependencies are missing.
+
+-------------------------------------------------------------------
Old:
----
1.3.0.tar.gz
New:
----
1.3.1.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-calmjs.parse.spec ++++++
--- /var/tmp/diff_new_pack.DUTDBM/_old 2023-11-13 22:20:33.942828624 +0100
+++ /var/tmp/diff_new_pack.DUTDBM/_new 2023-11-13 22:20:33.942828624 +0100
@@ -18,7 +18,7 @@
%{?sle15_python_module_pythons}
Name: python-calmjs.parse
-Version: 1.3.0
+Version: 1.3.1
Release: 0
Summary: Various parsers for ECMA standards
License: MIT
++++++ 1.3.0.tar.gz -> 1.3.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/calmjs.parse-1.3.0/.github/workflows/build.yml
new/calmjs.parse-1.3.1/.github/workflows/build.yml
--- old/calmjs.parse-1.3.0/.github/workflows/build.yml 2021-10-08
13:04:59.000000000 +0200
+++ new/calmjs.parse-1.3.1/.github/workflows/build.yml 2023-10-29
01:08:25.000000000 +0200
@@ -10,7 +10,7 @@
- 1.2.x
- 1.3.x
tags:
- - 1.3.0
+ - 1.3.1
pull_request:
branches:
- master
@@ -21,34 +21,48 @@
runs-on: ${{ matrix.os }}
strategy:
matrix:
- os: [ubuntu-latest, macos-latest]
- python-version: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9, "3.10", pypy2, pypy3]
+ os: [ubuntu-22.04, macos-latest]
+ python-version: [3.8, 3.9, "3.10", "3.11", "3.12", "pypy3.9"]
include:
+ - os: ubuntu-22.04
+ python-version: "2.7"
- os: windows-latest
python-version: 3.9
- os: windows-latest
python-version: "3.10"
+ - os: windows-latest
+ python-version: "3.11"
+ - os: windows-latest
+ python-version: "3.12"
exclude:
- os: macos-latest
python-version: 3.5
- os: macos-latest
python-version: 3.6
- os: macos-latest
- python-version: pypy2
- - os: macos-latest
- python-version: pypy3
+ python-version: "pypy3.9"
steps:
- - uses: actions/checkout@v2
- - name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v2
+ - uses: actions/checkout@v3
+ - name: Set up Python ${{ matrix.python-version }} via setup-python
+ if: matrix.python-version != '2.7'
+ uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
+ - name: Set up Python ${{ matrix.python-version }} via apt-get
+ if: matrix.python-version == '2.7'
+ run: |
+ set -eux
+ sudo apt-get update
+ sudo apt-get install -y python2 python3-virtualenv
+ virtualenv -p python2 "${{ runner.temp }}/venv"
+ echo "${{ runner.temp }}/venv/bin" >> $GITHUB_PATH
- name: Install dependencies
run: |
python -m pip install --upgrade pip
- python -m pip install coverage flake8
+ # must install all dependencies so the tab modules can be generated
+ python -m pip install coverage flake8 ply setuptools
python -m pip install -e .
- name: Lint with flake8
run: |
@@ -57,9 +71,13 @@
run: |
python -OO -m unittest calmjs.parse.tests.make_suite
coverage run --include=src/* -m unittest calmjs.parse.tests.make_suite
+ # Python 3.12 on Windows resulted in MemoryError here, so optional.
+ - name: Coverage report
+ run: |
coverage report -m
+ continue-on-error: true
- name: Coveralls
- if: ${{ matrix.os == 'ubuntu-latest' && matrix.python-version != '2.7'
&& matrix.python-version != 'pypy2' }}
+ if: ${{ matrix.os == 'ubuntu-22.04' && matrix.python-version != '2.7' &&
matrix.python-version != 'pypy2' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/calmjs.parse-1.3.0/CHANGES.rst
new/calmjs.parse-1.3.1/CHANGES.rst
--- old/calmjs.parse-1.3.0/CHANGES.rst 2021-10-08 13:04:59.000000000 +0200
+++ new/calmjs.parse-1.3.1/CHANGES.rst 2023-10-29 01:08:25.000000000 +0200
@@ -1,6 +1,16 @@
Changelog
=========
+1.3.1 - 2023-10-28
+------------------
+
+- Modified existing ``setup.py`` hook from an install hook to a build
+ hook to ensure the generated module files are present. Should any of
+ those modules are missing and the required dependencies for are not
+ present (i.e. ``ply`` and ``setuptools``), the build will result in a
+ non-zero exit status and the documented error message should reflect
+ which of the required dependencies are missing.
+
1.3.0 - 2021-10-08
------------------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/calmjs.parse-1.3.0/README.rst
new/calmjs.parse-1.3.1/README.rst
--- old/calmjs.parse-1.3.0/README.rst 2021-10-08 13:04:59.000000000 +0200
+++ new/calmjs.parse-1.3.1/README.rst 2023-10-29 01:08:25.000000000 +0200
@@ -5,12 +5,12 @@
ECMAScript; a near feature complete fork of |slimit|_. A CLI front-end
for this package is shipped separately as |crimp|_.
-.. image::
https://github.com/calmjs/calmjs.parse/actions/workflows/build.yml/badge.svg?branch=1.3.0
- :target:
https://github.com/calmjs/calmjs.parse/actions/workflows/build.yml?query=branch:1.3.0
-.. image::
https://ci.appveyor.com/api/projects/status/5dj8dnu9gmj02msu/branch/1.3.0?svg=true
- :target:
https://ci.appveyor.com/project/metatoaster/calmjs-parse/branch/1.3.0
-.. image::
https://coveralls.io/repos/github/calmjs/calmjs.parse/badge.svg?branch=1.3.0
- :target: https://coveralls.io/github/calmjs/calmjs.parse?branch=1.3.0
+.. image::
https://github.com/calmjs/calmjs.parse/actions/workflows/build.yml/badge.svg?branch=1.3.1
+ :target:
https://github.com/calmjs/calmjs.parse/actions/workflows/build.yml?query=branch:1.3.1
+.. image::
https://ci.appveyor.com/api/projects/status/5dj8dnu9gmj02msu/branch/1.3.1?svg=true
+ :target:
https://ci.appveyor.com/project/metatoaster/calmjs-parse/branch/1.3.1
+.. image::
https://coveralls.io/repos/github/calmjs/calmjs.parse/badge.svg?branch=1.3.1
+ :target: https://coveralls.io/github/calmjs/calmjs.parse?branch=1.3.1
.. |calmjs.parse| replace:: ``calmjs.parse``
.. |crimp| replace:: ``crimp``
@@ -78,9 +78,8 @@
modules for its lexer. The wheel distribution of |calmjs.parse| does
not require this extra step as it contains these pre-generated modules
for |ply| up to version 3.11 (the latest version available at the time
-of previous release), however the source tarball or if |ply| version
-that is installed lies outside of the supported versions, the following
-caveats will apply.
+of previous release), however the version of |ply| that is installed is
+beyond the supported version, the following caveats will apply.
If a more recent release of |ply| becomes available and the environment
upgrades to that version, those pre-generated modules may become
@@ -89,11 +88,18 @@
step if a newer version of |calmjs.parse| is not available, or |ply| may
be downgraded back to version 3.11 if possible.
+Alternatively, install a more recent version of |calmjs.parse| wheel
+that has the most complete set of pre-generated modules built.
+
Once the package is installed, the installation may be `tested`_ or be
`used directly`_.
-Alternative installation methods (for developers, advanced users)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Manual installation and packaging requirements
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+*This section is for developers and advanced users; contains important
+information for package maintainers for OS distributions (e.g. Linux)
+that will prevent less than ideal experiences for downstream users.*
Development is still ongoing with |calmjs.parse|, for the latest
features and bug fixes, the development version may be installed through
@@ -101,14 +107,58 @@
.. code:: console
- $ pip install
git+https://github.com/calmjs/calmjs.parse.git#egg=calmjs.parse
+ $ pip install ply setuptools # this MUST be done first; see below for
reason
+ $ pip install -e
git+https://github.com/calmjs/calmjs.parse.git#egg=calmjs.parse
+
+Note that all dependencies MUST be pre-installed ``setup.py build`` step
+to run, otherwise the build step required to create the pre-generated
+modules will result in failure.
+
+If |ply| isn't installed:
+
+.. code:: console
+
+ $ python -m pip install -e .
+ ...
+ running egg_info
+ ...
+ WARNING: cannot find distribution for 'ply'; using default value,
+ assuming 'ply==3.11' for pre-generated modules
+ ERROR: cannot find pre-generated modules for the assumed 'ply'
+ version from above and/or cannot `import ply` to build generated
+ modules, aborting build; please either ensure that the source
+ archive containing the pre-generate modules is being used, or that
+ the python package 'ply' is installed and available for import
+ before attempting to use the setup.py to build this package; please
+ refer to the top level README for further details
+
+If ``setuptools`` isn't installed:
+
+.. code:: console
+
+ $ python -m pip install -e .
+ ...
+ running egg_info
+ ...
+ Traceback (most recent call last):
+ ...
+ ModuleNotFoundError: No module named 'pkg_resources'
-Alternatively, the git repository can be cloned directly and execute
+Naturally, the git repository can be cloned directly and execute
``python setup.py develop`` while inside the root of the source
-directory.
+directory; again, both |ply| AND ``setuptools`` MUST already have be
+available for import.
-A manual optimization step may need to be performed for platforms and
-systems that do not have utf8 as their default encoding.
+As the git repository does NOT contain any pre-generated modules or
+code, the above message is likely to be seen by developers or distro
+maintainers who are on their first try at interacting with this
+software. However, the zip archives released on PyPI starting from
+version 1.3.0 do contain these modules fully pre-generated, thus they
+may be used as part of a standard installation step, i.e. without
+requiring |ply| be available for import before usage of the ``setup.py``
+for any purpose. While the same warning message about |ply| being
+missing may be shown, the pre-generated modules will allow the build
+step to proceed as normal.
Manual optimization
~~~~~~~~~~~~~~~~~~~
@@ -516,11 +566,13 @@
'4 + 4'
To assist with a more generalized usage, the ``ast_to_dict`` provides an
-additional ``fold_ops`` argument. When set to ``True``, various
-operators will be folded to assist with computing certain constants into
-a single computed value. This is often useful for ensuring concatenated
-strings are merged, and normalizing short-hand definition of boolean
-values via ``!0`` or ``!1``, among other commonly seen expressions.
+additional ``fold_ops`` argument. When set to ``True``, operator
+folding will be enabled on supported types; for example, constants will
+be attempted to be folded into a single value as per how operators are
+handled in the ECMAScript specification. This is often useful for
+ensuring concatenated strings are merged, and normalizing short-hand
+definition of boolean values via ``!0`` or ``!1``, among other commonly
+seen expressions.
.. code:: pycon
@@ -712,6 +764,7 @@
Further details and example usage can be consulted from the various
docstrings found within the module.
+
Limitations
-----------
@@ -733,6 +786,7 @@
statement will also be discarded as that is the second token consumed
by the production rule that produces a ``Conditional`` node.
+
Troubleshooting
---------------
@@ -766,6 +820,59 @@
Further details on this topic may be found in the `manual optimization`_
section of this document.
+WARNING: There are unused tokens on import
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This indicates that the installation method or source for this package
+being imported isn't optimized. A quick workaround is to follow the
+instructions at the `manual optimization`_ section of this document to
+ensure these messages are no longer generated (and if this warning
+happens every time the module is imported, it means the symbol tables
+are regenerated every time that happens and this extra computational
+overhead should be corrected through the generation of that optimization
+module).
+
+The optimization modules are included with the wheel release and the
+source release on PyPI, but it is not part of the source repository as
+generated code are never committed. Should a binary release made by
+a third-party results in this warning upon import, their release should
+be corrected to include the optimization module.
+
+Moreover, there are safeguards in place that prevent this warning from
+being generated for releases made for releases from 1.3.1 onwards by
+a more heavy handed enforcement of this optimization step at build time,
+but persistent (or careless) actors may circumvent this during the build
+process, but official releases made through PyPI should include the
+required optimization for all supported |ply| versions (which are
+versions 3.6 to 3.11, inclusive).
+
+Alternatively, this issue may also occur via usage of ``pyinstaller``
+if the package metadata is not copied for |ply| in versions prior to
+``calmjs.parse-1.3.1`` and will always occur if the hidden imports are
+not declared for those optimization modules. The following hook should
+may be used to ensure |calmjs.parse| functions correctly in the compiled
+binary:
+
+.. code:: python
+
+ from PyInstaller.utils.hooks import collect_data_files, copy_metadata
+ from calmjs.parse.utils import generate_tab_names
+
+ datas = []
+ datas.extend(collect_data_files("ply"))
+ datas.extend(copy_metadata("ply"))
+ datas.extend(collect_data_files("calmjs.parse"))
+ datas.extend(copy_metadata("calmjs.parse"))
+
+ hiddenimports = []
+ hiddenimports.extend(generate_tab_names('calmjs.parse.parsers.es5'))
+
+ # if running under Python 3 with ply-3.11, above is equivalent to
+ # hiddenimports = [
+ # "calmjs.parse.parsers.lextab_es5_py3_ply3_11",
+ # "calmjs.parse.parsers.yacctab_es5_py3_ply3_11",
+ # ]
+
Slow performance
~~~~~~~~~~~~~~~~
@@ -779,6 +886,18 @@
arguments with name collisions, and the new function will take in all
of those arguments in one go.
+ERROR message about import error when trying to install
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+As noted in the error message, the |ply|_ and ``setuptools`` package
+must be installed before attempting to install build the package in the
+situation where the pre-generated modules are missing. This situation
+may be caused by building directly using the source provided by the
+source code repository, or where there is no matching pre-generated
+module matching with the installed version of |ply|. Please ensure that
+|ply| is installed and available first before installing from source if
+this error message is sighted.
+
Contribute
----------
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/calmjs.parse-1.3.0/appveyor.yml
new/calmjs.parse-1.3.1/appveyor.yml
--- old/calmjs.parse-1.3.0/appveyor.yml 2021-10-08 13:04:59.000000000 +0200
+++ new/calmjs.parse-1.3.1/appveyor.yml 2023-10-29 01:08:25.000000000 +0200
@@ -1,24 +1,21 @@
+image: Visual Studio 2022
+
environment:
matrix:
- PYTHON: "C:\\Python27"
- nodejs_version: "4.6"
- PYTHON: "C:\\Python33"
- nodejs_version: "4.6"
- PYTHON: "C:\\Python34"
- nodejs_version: "6.9"
- PYTHON: "C:\\Python35"
- nodejs_version: "6.9"
- PYTHON: "C:\\Python36"
- nodejs_version: "8"
- PYTHON: "C:\\Python37"
- nodejs_version: "10"
- PYTHON: "C:\\Python38"
- nodejs_version: "10"
+ - PYTHON: "C:\\Python39-x64"
+ - PYTHON: "C:\\Python310-x64"
+ - PYTHON: "C:\\Python311-x64"
install:
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
- - ps: Install-Product node $env:nodejs_version
- - "%PYTHON%\\python.exe -m pip install coverage"
+ - "%PYTHON%\\python.exe -m pip install setuptools coverage ply"
- "%PYTHON%\\python.exe setup.py install"
test_script:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/calmjs.parse-1.3.0/setup.py
new/calmjs.parse-1.3.1/setup.py
--- old/calmjs.parse-1.3.0/setup.py 2021-10-08 13:04:59.000000000 +0200
+++ new/calmjs.parse-1.3.1/setup.py 2023-10-29 01:08:25.000000000 +0200
@@ -1,19 +1,27 @@
-import atexit
+import os
import sys
from setuptools import setup, find_packages
-from setuptools.command.install import install
+from setuptools.command.build_py import build_py
from subprocess import call
-class InstallHook(install):
- """For hooking the optimizer when setup exits"""
+class BuildHook(build_py):
+ """Forcing the optimizer to run before the build step"""
def __init__(self, *a, **kw):
- install.__init__(self, *a, **kw)
- atexit.register(
- call, [sys.executable, '-m', 'calmjs.parse.parsers.optimize'])
+ # must use clone of this, otherwise Python on Windows gets sad.
+ env = os.environ.copy()
+ env['PYTHONPATH'] = 'src'
+ code = call([
+ sys.executable, '-m', 'calmjs.parse.parsers.optimize', '--build'
+ ], env=env)
+ if code:
+ sys.exit(1)
+ build_py.__init__(self, *a, **kw)
-version = '1.3.0'
+# Attributes
+
+version = '1.3.1'
classifiers = """
Development Status :: 5 - Production/Stable
@@ -28,6 +36,10 @@
Programming Language :: Python :: 3.6
Programming Language :: Python :: 3.7
Programming Language :: Python :: 3.8
+Programming Language :: Python :: 3.9
+Programming Language :: Python :: 3.10
+Programming Language :: Python :: 3.11
+Programming Language :: Python :: 3.12
""".strip().splitlines()
long_description = (
@@ -55,7 +67,7 @@
include_package_data=True,
zip_safe=False,
cmdclass={
- 'install': InstallHook,
+ 'build_py': BuildHook,
},
install_requires=[
'setuptools',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/calmjs.parse-1.3.0/src/calmjs/parse/__init__.py
new/calmjs.parse-1.3.1/src/calmjs/parse/__init__.py
--- old/calmjs.parse-1.3.0/src/calmjs/parse/__init__.py 2021-10-08
13:04:59.000000000 +0200
+++ new/calmjs.parse-1.3.1/src/calmjs/parse/__init__.py 2023-10-29
01:08:25.000000000 +0200
@@ -3,7 +3,14 @@
Quick access helper functions
"""
-from calmjs.parse.factory import ParserUnparserFactory
+try:
+ from calmjs.parse.factory import ParserUnparserFactory
+except ImportError as e: # pragma: no cover
+ exc = e
+ def import_error(*a, **kw):
+ raise exc
-es5 = ParserUnparserFactory('es5', 'pretty_print', 'minify_print')
+ es5 = import_error
+else:
+ es5 = ParserUnparserFactory('es5', 'pretty_print', 'minify_print')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/calmjs.parse-1.3.0/src/calmjs/parse/parsers/es5.py
new/calmjs.parse-1.3.1/src/calmjs/parse/parsers/es5.py
--- old/calmjs.parse-1.3.0/src/calmjs/parse/parsers/es5.py 2021-10-08
13:04:59.000000000 +0200
+++ new/calmjs.parse-1.3.1/src/calmjs/parse/parsers/es5.py 2023-10-29
01:08:25.000000000 +0200
@@ -42,8 +42,10 @@
asttypes = AstTypesFactory(pretty_print, ReprWalker())
-# The default values for the `Parser` constructor, passed on to ply; they must
-# be strings
+# These default values for the `Parser` constructor, passed on to ply;
+# they must be strings; these values are for reference only as
+# modifications to this value will not change what's been set up as
+# the Parser's default.
lextab, yacctab = generate_tab_names(__name__)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/calmjs.parse-1.3.0/src/calmjs/parse/parsers/optimize.py
new/calmjs.parse-1.3.1/src/calmjs/parse/parsers/optimize.py
--- old/calmjs.parse-1.3.0/src/calmjs/parse/parsers/optimize.py 2021-10-08
13:04:59.000000000 +0200
+++ new/calmjs.parse-1.3.1/src/calmjs/parse/parsers/optimize.py 2023-10-29
01:08:25.000000000 +0200
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
"""
-Helpers that will forcibly regenerate the tab files.
+Helpers for maintenance/generation of the lextab/yacctab modules.
The original goal of this was to force the creation of tab files using
the utf8 codec to workaround issues with the ply package, for systems
@@ -8,19 +8,36 @@
"""
import codecs
+import os
import sys
from functools import partial
from os import unlink
from os.path import exists
-from ply import lex
from importlib import import_module
+from calmjs.parse.utils import generate_tab_names
+from calmjs.parse.utils import ply_dist
-# have to do this for every parser modules
-from calmjs.parse.parsers import es5
+_ASSUME_PLY_VERSION = '3.11'
+_ASSUME_ENVVAR = 'CALMJS_PARSE_ASSUME_PLY_VERSION'
-def purge_tabs(module):
+def validate_imports(*imports):
paths = []
+ missing = []
+ for name in imports:
+ try:
+ import_module(name)
+ except ImportError:
+ missing.append(name)
+ else:
+ paths.append(sys.modules.pop(name).__file__)
+ return paths, missing
+
+
+def find_tab_paths(module):
+ # return a list of lextab/yacctab module paths and a list of missing
+ # import names.
+ names = []
for entry in ('lextab', 'yacctab'):
# we assume the specified entries are defined as such
name = getattr(module, entry)
@@ -31,14 +48,12 @@
'provided module `%s` does not export expected tab values ' %
module.__name__
)
- try:
- import_module(name)
- except ImportError:
- # don't need to do anything
- pass
- else:
- paths.append(sys.modules.pop(name).__file__)
+ names.append(name)
+ return validate_imports(*names)
+
+def purge_tabs(module):
+ paths, _ = find_tab_paths(module)
unlink_modules(verify_paths(paths))
@@ -46,10 +61,15 @@
for path in paths:
if exists(path):
yield path
+ # locate any adjacent .py[co]? files based on module path
+ # returned; mostly a problem with Python 2
if path[-4:] in ('.pyc', '.pyo'):
- # find the .py file, too.
if exists(path[:-1]):
yield path[:-1]
+ else:
+ for c in 'co':
+ if exists(path + c):
+ yield path + c
def unlink_modules(paths):
@@ -64,13 +84,118 @@
module.Parser()
-def reoptimize_all(monkey_patch=False):
+def _assume_ply_version():
+ version = os.environ.get(_ASSUME_ENVVAR, _ASSUME_PLY_VERSION)
+ if ply_dist is None:
+ if _ASSUME_ENVVAR in os.environ:
+ source = "using environment variable %r" % _ASSUME_ENVVAR
+ else:
+ # allow bypassing of setuptools as ply provides this
+ # attribute
+ try:
+ import ply
+ version = ply.__version__
+ source = "using value provided by ply"
+ except ImportError: # pragma: no cover
+ ply = None
+ source = "using default value"
+
+ sys.stderr.write(
+ u"WARNING: cannot find distribution for 'ply'; "
+ "%s, assuming 'ply==%s' for pre-generated modules\n" % (
+ source, version))
+ return version
+
+
+def optimize_build(module_name, assume_ply_version=True):
+ """
+ optimize build helper for first build
+
+ assume_ply_version
+ This flag denotes whether or not to assume a ply version should
+ ply be NOT installed; this will either assume ply to be whatever
+ value assigned to _ASSUME_PLY_VERSION (i.e. 3.11), or read from
+ the environment variable `CALMJS_PARSE_ASSUME_PLY_VERSION`.
+
+ The goal is to allow the build to proceed if the pre-generated
+ files are already present, before the dependency resolution at
+ the installation time actually kicks in to install ply.
+
+ Default: True
+ """
+
+ kws = {}
+ if assume_ply_version:
+ kws['_version'] = _assume_ply_version()
+
+ lextab, yacctab = generate_tab_names(module_name, **kws)
+ paths, missing = validate_imports(lextab, yacctab)
+ if missing:
+ # only import, purge and regenerate if any are missing.
+ unlink_modules(verify_paths(paths))
+ module = import_module(module_name)
+ # use whatever assumed version or otherwise as set up by
+ # the local generation function.
+ module.Parser(lextab=lextab, yacctab=yacctab)
+
+
+def reoptimize_all(monkey_patch=False, first_build=False):
+ """
+ The main optimize method for maintainence of the generated tab
+ modules required by ply
+
+ Arguments:
+
+ monkey_patch
+ patches the default open function in ply.lex to use utf8
+
+ default: False
+
+ first_build
+ flag for switching between reoptimize/optimize_build method;
+ setting the flag to True specifies the latter.
+
+ default: False
+ """
+
if monkey_patch:
- lex.open = partial(codecs.open, encoding='utf8')
- modules = (es5,)
- for module in modules:
- reoptimize(module)
+ try:
+ from ply import lex
+ except ImportError: # pragma: no cover
+ pass # fail later; only fail if import ply is truly needed
+ else:
+ lex.open = partial(codecs.open, encoding='utf8')
+
+ modules = ('.es5',)
+ try:
+ for name in modules:
+ if first_build:
+ # A consideration for modifying this flag to simply
+ # check for a marker file to denote none of this being
+ # needed (i.e. this tarball was fully prepared), but it
+ # will not solve the issue where the distro packager
+ # already got an even more recent version of ply
+ # installed (as unlikely as that is) and that build step
+ # then is completely skipped.
+ optimize_build('calmjs.parse.parsers' + name)
+ else:
+ module = import_module(name, 'calmjs.parse.parsers')
+ reoptimize(module)
+ except ImportError as e:
+ if not first_build or 'ply' not in str(e):
+ raise
+ sys.stderr.write(
+ u"ERROR: cannot find pre-generated modules for the assumed 'ply' "
+ "version from above and/or cannot `import ply` to build generated "
+ "modules, aborting build; please either ensure that the source "
+ "archive containing the pre-generate modules is being used, or "
+ "that the python package 'ply' is installed and available for "
+ "import before attempting to use the setup.py to build this "
+ "package; please refer to the top level README for further "
+ "details\n"
+ )
+ sys.exit(1)
if __name__ == '__main__': # pragma: no cover
- reoptimize_all(True)
+ reoptimize_all(True, '--build' in sys.argv)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/calmjs.parse-1.3.0/src/calmjs/parse/tests/__init__.py
new/calmjs.parse-1.3.1/src/calmjs/parse/tests/__init__.py
--- old/calmjs.parse-1.3.0/src/calmjs/parse/tests/__init__.py 2021-10-08
13:04:59.000000000 +0200
+++ new/calmjs.parse-1.3.1/src/calmjs/parse/tests/__init__.py 2023-10-29
01:08:25.000000000 +0200
@@ -65,18 +65,25 @@
'calmjs.parse.tests', pattern='test_*.py',
top_level_dir=dirname(__file__)
)
- test_suite.addTest(doctest.DocTestSuite(es5lexer, optionflags=optflags))
- test_suite.addTest(doctest.DocTestSuite(walkers, optionflags=optflags))
- test_suite.addTest(doctest.DocTestSuite(sourcemap, optionflags=optflags))
- test_suite.addTest(doctest.DocTestCase(
- # skipping all the error case tests which should all be in the
- # troubleshooting section at the end; bump the index whenever
- # more failure examples are added.
- # also note that line number is unknown, as PKG_INFO has headers
- # and also the counter is somehow inaccurate in this case.
- doctest.DocTest(pkgdesc_tests[:-1], {
- 'open': open}, 'PKG_INFO', 'README.rst', None, pkgdesc),
- optionflags=optflags,
- ))
+ try:
+ test_suite.addTest(doctest.DocTestSuite(
+ es5lexer, optionflags=optflags))
+ test_suite.addTest(doctest.DocTestSuite(
+ walkers, optionflags=optflags))
+ test_suite.addTest(doctest.DocTestSuite(
+ sourcemap, optionflags=optflags))
+ test_suite.addTest(doctest.DocTestCase(
+ # skipping all the error case tests which should all be in the
+ # troubleshooting section at the end; bump the index whenever
+ # more failure examples are added.
+ # also note that line number is unknown, as PKG_INFO has headers
+ # and also the counter is somehow inaccurate in this case.
+ doctest.DocTest(pkgdesc_tests[:-1], {
+ 'open': open}, 'PKG_INFO', 'README.rst', None, pkgdesc),
+ optionflags=optflags,
+ ))
+ except AttributeError:
+ # Assuming this is in Python>3.9 where the -OO flag was used...
+ pass
return test_suite
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/calmjs.parse-1.3.0/src/calmjs/parse/tests/test_parsers_optimize.py
new/calmjs.parse-1.3.1/src/calmjs/parse/tests/test_parsers_optimize.py
--- old/calmjs.parse-1.3.0/src/calmjs/parse/tests/test_parsers_optimize.py
2021-10-08 13:04:59.000000000 +0200
+++ new/calmjs.parse-1.3.1/src/calmjs/parse/tests/test_parsers_optimize.py
2023-10-29 01:08:25.000000000 +0200
@@ -5,12 +5,14 @@
import os
import sys
+from io import StringIO
from shutil import rmtree
from tempfile import mkdtemp
from types import ModuleType
from ply import lex
from calmjs.parse.parsers import optimize
from calmjs.parse.parsers import es5
+from calmjs.parse.utils import ply_dist
class OptimizeTestCase(unittest.TestCase):
@@ -22,16 +24,26 @@
def tearDown(self):
optimize.unlink = os.unlink
optimize.import_module = importlib.import_module
+ optimize.ply_dist = ply_dist
# undo whatever monkey patch that may have happened
lex.open = open
+ def break_ply(self):
+ import ply
+
+ def cleanup():
+ sys.modules['ply'] = ply
+
+ self.addCleanup(cleanup)
+ sys.modules['ply'] = None
+
def test_verify_paths(self):
tempdir = mkdtemp()
self.addCleanup(rmtree, tempdir)
# create fake module files
modules = [os.path.join(tempdir, name) for name in (
- 'foo.pyc', 'bar.pyc', 'foo.py')]
+ 'foo.pyc', 'bar.py', 'bar.pyc', 'foo.py')]
for module in modules:
with open(module, 'w'):
@@ -42,11 +54,28 @@
sorted(optimize.verify_paths(modules[:2])),
)
- def test_unlink_modules(self):
+ def test_find_tab_paths(self):
+ fake_es5 = ModuleType('fake_es5')
+ fake_es5.lextab = 'some_lextab'
+ fake_es5.yacctab = 'some_yacctab'
+ paths, missing = optimize.find_tab_paths(fake_es5)
+ self.assertEqual([], paths)
+ self.assertEqual(['some_lextab', 'some_yacctab'], missing)
+
# ensure the parser exists
es5.Parser()
# should have created the optimized version of the file, if not
# already exists
+ answers = [
+ sys.modules[es5.lextab].__file__,
+ sys.modules[es5.yacctab].__file__,
+ ]
+ paths, missing = optimize.find_tab_paths(es5)
+ self.assertEqual(paths, answers)
+ self.assertEqual([], missing)
+
+ def test_unlink_modules(self):
+ es5.Parser()
p = sys.modules[es5.yacctab].__file__
self.assertTrue(os.path.exists(p))
# unlink has been patched out
@@ -87,3 +116,166 @@
def test_reoptimize_monkey_patched(self):
optimize.reoptimize_all(True)
self.assertIsNot(lex.open, open)
+ self.assertNotEqual(len(self.purged), 0)
+
+ def test_optimize_build(self):
+ called = []
+
+ def sentinel(*a, **kw):
+ called.append(True)
+
+ fake_es5 = ModuleType('fake_namespace.fake_es5')
+ fake_es5.Parser = sentinel
+
+ # inject fake namespace and module
+ sys.modules['fake_namespace'] = ModuleType('fake_namespace')
+ self.addCleanup(sys.modules.pop, 'fake_namespace')
+ sys.modules['fake_namespace.fake_es5'] = fake_es5
+ self.addCleanup(sys.modules.pop, 'fake_namespace.fake_es5')
+
+ optimize.optimize_build('fake_namespace.fake_es5')
+ self.assertEqual(len(self.purged), 0)
+ self.assertTrue(called)
+
+ def test_optimize_first_build(self):
+ optimize.reoptimize_all(True, first_build=True)
+ # shouldn't have purged any modules
+ self.assertEqual(len(self.purged), 0)
+
+ def test_optimize_first_build_valid_with_broken_ply(self):
+ self.break_ply()
+ optimize.reoptimize_all(True, first_build=True)
+ # shouldn't have purged any modules
+ self.assertEqual(len(self.purged), 0)
+
+ def test_assume_ply_version_default_ply(self):
+ # only applicable if no ply_dist found
+ optimize.ply_dist = None
+ stderr = sys.stderr
+ self.addCleanup(setattr, sys, 'stderr', stderr)
+
+ # where ply is actually available; and since the real thing is
+ # expected to be present and usable, intersperse that real value
+ # into the expected string.
+ import ply
+ sys.stderr = StringIO()
+ optimize._assume_ply_version()
+ self.assertTrue(sys.stderr.getvalue().startswith(
+ "WARNING: cannot find distribution for 'ply'; using value "
+ "provided by ply, assuming 'ply==%s' for pre-generated modules" % (
+ ply.__version__
+ )))
+
+ def test_assume_ply_version_override_ply(self):
+ # can still override if ply is actually available
+ optimize.ply_dist = None
+ stderr = sys.stderr
+ self.addCleanup(setattr, sys, 'stderr', stderr)
+
+ self.addCleanup(os.environ.pop, optimize._ASSUME_ENVVAR, None)
+ sys.stderr = StringIO()
+ os.environ[optimize._ASSUME_ENVVAR] = '0.9999' # should never exist
+ optimize._assume_ply_version()
+ self.assertTrue(sys.stderr.getvalue().startswith(
+ "WARNING: cannot find distribution for 'ply'; using environment "
+ "variable 'CALMJS_PARSE_ASSUME_PLY_VERSION', "
+ "assuming 'ply==0.9999' for pre-generated modules"))
+
+ def test_assume_ply_version_no_ply(self):
+ # default when ply is fully broken.
+ optimize.ply_dist = None
+ stderr = sys.stderr
+ self.addCleanup(setattr, sys, 'stderr', stderr)
+
+ self.break_ply()
+ sys.stderr = StringIO()
+ optimize._assume_ply_version()
+ self.assertTrue(sys.stderr.getvalue().startswith(
+ "WARNING: cannot find distribution for 'ply'; using default "
+ "value, assuming 'ply==3.11' for pre-generated modules"))
+
+ def test_optimize_first_build_valid_with_broken_ply_error(self):
+ def fail_import(*a, **kw):
+ raise ImportError('no module named ply')
+
+ optimize.import_module = fail_import
+
+ with self.assertRaises(ImportError):
+ optimize.reoptimize_all()
+
+ stderr = sys.stderr
+
+ def cleanup():
+ sys.stderr = stderr
+
+ self.addCleanup(cleanup)
+
+ sys.stderr = StringIO()
+ with self.assertRaises(SystemExit):
+ optimize.reoptimize_all(first_build=True)
+
+ self.assertTrue(sys.stderr.getvalue().startswith(
+ "ERROR: cannot find pre-generated modules for the assumed 'ply' "
+ "version"))
+
+ def test_optimize_first_build_assume_broken_ply_error(self):
+ optimize.ply_dist = None
+
+ self.break_ply()
+
+ def fail_import(*a, **kw):
+ raise ImportError('no module named ply')
+
+ optimize.import_module = fail_import
+
+ with self.assertRaises(ImportError):
+ optimize.reoptimize_all()
+
+ stderr = sys.stderr
+
+ def cleanup():
+ sys.stderr = stderr
+
+ self.addCleanup(cleanup)
+
+ sys.stderr = StringIO()
+ with self.assertRaises(SystemExit):
+ optimize.reoptimize_all(first_build=True)
+
+ lines = sys.stderr.getvalue().splitlines()
+ self.assertTrue(lines[0].startswith(
+ "WARNING: cannot find distribution for 'ply'; using default value"
+ ))
+ self.assertTrue(lines[1].startswith(
+ "ERROR: cannot find pre-generated modules for the assumed 'ply' "
+ "version"))
+
+ def test_optimize_build_assume_broken_ply_but_available(self):
+ optimize.ply_dist = None
+ called = []
+
+ def sentinel(*a, **kw):
+ called.append(True)
+
+ fake_es5 = ModuleType('fake_namespace.fake_es5')
+ fake_es5.Parser = sentinel
+
+ # inject fake namespace and module
+ sys.modules['fake_namespace'] = ModuleType('fake_namespace')
+ self.addCleanup(sys.modules.pop, 'fake_namespace')
+ sys.modules['fake_namespace.fake_es5'] = fake_es5
+ self.addCleanup(sys.modules.pop, 'fake_namespace.fake_es5')
+ stderr = sys.stderr
+ self.addCleanup(setattr, sys, 'stderr', stderr)
+ sys.stderr = StringIO()
+
+ optimize.optimize_build('fake_namespace.fake_es5')
+ self.assertEqual(len(self.purged), 0)
+ self.assertTrue(called)
+ # this parser will not actually error as it does nothing; and
+ # so not actually care whether ply actually available here or
+ # not.
+ self.assertTrue(sys.stderr.getvalue().startswith(
+ "WARNING: cannot find distribution for 'ply'; "
+ ))
+ self.assertNotIn('ERROR', sys.stderr.getvalue())
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/calmjs.parse-1.3.0/src/calmjs/parse/tests/test_walkers.py
new/calmjs.parse-1.3.1/src/calmjs/parse/tests/test_walkers.py
--- old/calmjs.parse-1.3.0/src/calmjs/parse/tests/test_walkers.py
2021-10-08 13:04:59.000000000 +0200
+++ new/calmjs.parse-1.3.1/src/calmjs/parse/tests/test_walkers.py
2023-10-29 01:08:25.000000000 +0200
@@ -218,3 +218,16 @@
"]>"
)
+
+ def test_walker_skip(self):
+ t = es5('''
+ a = 1;
+ b = '2';
+ ''')
+ self.assertEqual(str(walker.extract(
+ t, lambda n: isinstance(n, asttypes.Assign), skip=1)), "b = '2'")
+
+ with self.assertRaises(TypeError) as e:
+ walker.extract(t, lambda n: isinstance(n, asttypes.Assign), skip=2)
+
+ self.assertEqual(str(e.exception), 'no match found')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/calmjs.parse-1.3.0/src/calmjs/parse/unparsers/extractor.py
new/calmjs.parse-1.3.1/src/calmjs/parse/unparsers/extractor.py
--- old/calmjs.parse-1.3.0/src/calmjs/parse/unparsers/extractor.py
2021-10-08 13:04:59.000000000 +0200
+++ new/calmjs.parse-1.3.1/src/calmjs/parse/unparsers/extractor.py
2023-10-29 01:08:25.000000000 +0200
@@ -76,7 +76,6 @@
# See ECMA-262 5.1 Edition, Section 9
# Note that this parser does not provide an undefined token or global
# binding, it's currently not handled.
-# The hint argument is the PreferredType
def value_to_str(value):
"""
@@ -111,7 +110,7 @@
def to_primitive(fragment, hint):
- # TODO implement the correct return value for either Object/Array
+ # The hint argument is the PreferredType
if (issubclass(fragment.folded_type, Array) or
issubclass(fragment.folded_type, Object)):
value = value_to_str(fragment.value)
@@ -794,7 +793,6 @@
"""
def __call__(self, walk, dispatcher, node):
- # TODO this is getting similar with AsDict
misc_chunks = defaultdict(list)
nodes = iter(node)
for target_node in nodes:
@@ -1049,7 +1047,6 @@
),),
),
'FuncDecl': (
- # TODO DeclareAsFunc?
GroupAsAssignment((
Attr(Declare('identifier')),
PushScope,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/calmjs.parse-1.3.0/src/calmjs/parse/utils.py
new/calmjs.parse-1.3.1/src/calmjs/parse/utils.py
--- old/calmjs.parse-1.3.0/src/calmjs/parse/utils.py 2021-10-08
13:04:59.000000000 +0200
+++ new/calmjs.parse-1.3.1/src/calmjs/parse/utils.py 2023-10-29
01:08:25.000000000 +0200
@@ -13,6 +13,14 @@
from pkg_resources import working_set
from pkg_resources import Requirement
ply_dist = working_set.find(Requirement.parse('ply'))
+ # note that for **extremely** ancient versions of setuptools, e.g.
+ # setuptools<0.6c11, or some very non-standard environment that does
+ # not include the required metadata (e.g. pyinstaller without the
+ # required metadata), will require the following workaround...
+ if ply_dist is None: # pragma: no cover
+ from pkg_resources import Distribution
+ import ply
+ ply_dist = Distribution(project_name='ply', version=ply.__version__)
except ImportError: # pragma: no cover
ply_dist = None
@@ -34,7 +42,7 @@
return repr(s)
-def generate_tab_names(name):
+def generate_tab_names(name, _version='unknown'):
"""
Return the names to lextab and yacctab modules for the given module
name. Typical usage should be like so::
@@ -44,8 +52,8 @@
package_name, module_name = name.rsplit('.', 1)
- version = ply_dist.version.replace(
- '.', '_') if ply_dist is not None else 'unknown'
+ version = (ply_dist.version if ply_dist is not None else _version).replace(
+ '.', '_')
data = (package_name, module_name, py_major, version)
lextab = '%s.lextab_%s_py%d_ply%s' % data
yacctab = '%s.yacctab_%s_py%d_ply%s' % data