mirror of
https://github.com/python/cpython.git
synced 2025-10-01 21:02:15 +00:00
bpo-24960: use pkgutil.get_data in lib2to3 to read pickled grammar files (GH-4977) (#4979)
This is more complicated than it should be because we need to preserve the
useful mtime-based regeneration feature that lib2to3.pgen2.driver.load_grammar
has. We only look for the pickled grammar file with pkgutil.get_data and only if
the source file does not exist.
(cherry picked from commit 8a5877165e
)
This commit is contained in:
parent
2e1ef00171
commit
c1b8eb8006
5 changed files with 45 additions and 2 deletions
|
@ -20,6 +20,7 @@ import codecs
|
||||||
import io
|
import io
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
|
import pkgutil
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
# Pgen imports
|
# Pgen imports
|
||||||
|
@ -143,6 +144,26 @@ def _newer(a, b):
|
||||||
return os.path.getmtime(a) >= os.path.getmtime(b)
|
return os.path.getmtime(a) >= os.path.getmtime(b)
|
||||||
|
|
||||||
|
|
||||||
|
def load_packaged_grammar(package, grammar_source):
|
||||||
|
"""Normally, loads a pickled grammar by doing
|
||||||
|
pkgutil.get_data(package, pickled_grammar)
|
||||||
|
where *pickled_grammar* is computed from *grammar_source* by adding the
|
||||||
|
Python version and using a ``.pickle`` extension.
|
||||||
|
|
||||||
|
However, if *grammar_source* is an extant file, load_grammar(grammar_source)
|
||||||
|
is called instead. This facilities using a packaged grammar file when needed
|
||||||
|
but preserves load_grammar's automatic regeneration behavior when possible.
|
||||||
|
|
||||||
|
"""
|
||||||
|
if os.path.isfile(grammar_source):
|
||||||
|
return load_grammar(grammar_source)
|
||||||
|
pickled_name = _generate_pickle_name(os.path.basename(grammar_source))
|
||||||
|
data = pkgutil.get_data(package, pickled_name)
|
||||||
|
g = grammar.Grammar()
|
||||||
|
g.loads(data)
|
||||||
|
return g
|
||||||
|
|
||||||
|
|
||||||
def main(*args):
|
def main(*args):
|
||||||
"""Main program, when run as a script: produce grammar pickle files.
|
"""Main program, when run as a script: produce grammar pickle files.
|
||||||
|
|
||||||
|
|
|
@ -108,6 +108,10 @@ class Grammar(object):
|
||||||
d = pickle.load(f)
|
d = pickle.load(f)
|
||||||
self.__dict__.update(d)
|
self.__dict__.update(d)
|
||||||
|
|
||||||
|
def loads(self, pkl):
|
||||||
|
"""Load the grammar tables from a pickle bytes object."""
|
||||||
|
self.__dict__.update(pickle.loads(pkl))
|
||||||
|
|
||||||
def copy(self):
|
def copy(self):
|
||||||
"""
|
"""
|
||||||
Copy the grammar.
|
Copy the grammar.
|
||||||
|
|
|
@ -29,12 +29,12 @@ class Symbols(object):
|
||||||
setattr(self, name, symbol)
|
setattr(self, name, symbol)
|
||||||
|
|
||||||
|
|
||||||
python_grammar = driver.load_grammar(_GRAMMAR_FILE)
|
python_grammar = driver.load_packaged_grammar("lib2to3", _GRAMMAR_FILE)
|
||||||
|
|
||||||
python_symbols = Symbols(python_grammar)
|
python_symbols = Symbols(python_grammar)
|
||||||
|
|
||||||
python_grammar_no_print_statement = python_grammar.copy()
|
python_grammar_no_print_statement = python_grammar.copy()
|
||||||
del python_grammar_no_print_statement.keywords["print"]
|
del python_grammar_no_print_statement.keywords["print"]
|
||||||
|
|
||||||
pattern_grammar = driver.load_grammar(_PATTERN_GRAMMAR_FILE)
|
pattern_grammar = driver.load_packaged_grammar("lib2to3", _PATTERN_GRAMMAR_FILE)
|
||||||
pattern_symbols = Symbols(pattern_grammar)
|
pattern_symbols = Symbols(pattern_grammar)
|
||||||
|
|
|
@ -12,7 +12,10 @@ from .support import driver
|
||||||
from test.support import verbose
|
from test.support import verbose
|
||||||
|
|
||||||
# Python imports
|
# Python imports
|
||||||
|
import importlib
|
||||||
|
import operator
|
||||||
import os
|
import os
|
||||||
|
import pickle
|
||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
@ -99,6 +102,18 @@ pgen2_driver.load_grammar(%r, save=True, force=True)
|
||||||
finally:
|
finally:
|
||||||
shutil.rmtree(tmpdir)
|
shutil.rmtree(tmpdir)
|
||||||
|
|
||||||
|
def test_load_packaged_grammar(self):
|
||||||
|
modname = __name__ + '.load_test'
|
||||||
|
class MyLoader:
|
||||||
|
def get_data(self, where):
|
||||||
|
return pickle.dumps({'elephant': 19})
|
||||||
|
class MyModule:
|
||||||
|
__file__ = 'parsertestmodule'
|
||||||
|
__spec__ = importlib.util.spec_from_loader(modname, MyLoader())
|
||||||
|
sys.modules[modname] = MyModule()
|
||||||
|
self.addCleanup(operator.delitem, sys.modules, modname)
|
||||||
|
g = pgen2_driver.load_packaged_grammar(modname, 'Grammar.txt')
|
||||||
|
self.assertEqual(g.elephant, 19)
|
||||||
|
|
||||||
|
|
||||||
class GrammarTest(support.TestCase):
|
class GrammarTest(support.TestCase):
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
2to3 and lib2to3 can now read pickled grammar files using pkgutil.get_data()
|
||||||
|
rather than probing the filesystem. This lets 2to3 and lib2to3 work when run
|
||||||
|
from a zipfile.
|
Loading…
Add table
Add a link
Reference in a new issue