Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upgrade Antlr to 4.11 and vendor the runtime #1114

Merged
merged 51 commits into from
Feb 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
ab63619
replace antlr binary
Aug 9, 2023
3bc6bd5
Update references to antler 4.9 to 4.11
Aug 9, 2023
bc4949b
add news fragment
Jasha10 Aug 19, 2023
ec1dd16
Add helper script for vendoring antlr4
jlopezpena Oct 10, 2023
635e01a
missing file
jlopezpena Oct 10, 2023
1040c72
cleanups
jlopezpena Oct 10, 2023
09ce997
Fix imports in omegaconf and remove antlr dep from base.txt
jlopezpena Oct 10, 2023
62c2b13
Fix imports on generated parsers
jlopezpena Oct 10, 2023
9b9272d
unused f-string
jlopezpena Oct 10, 2023
b234191
typo in comment
jlopezpena Oct 10, 2023
7267356
Mention vendoring in the news item
jlopezpena Oct 10, 2023
024cef1
Add vendored files (just to test CI, revert if we decide to pull at b…
jlopezpena Oct 10, 2023
5531cf0
Add readme to vendor folder
jlopezpena Oct 10, 2023
e85bade
ignore mypy errors in vendored code
jlopezpena Oct 10, 2023
5708f94
Add vendored package to setup.py
jlopezpena Oct 10, 2023
8b18374
ignore vendored in coverage, flake, and isort
jlopezpena Oct 10, 2023
a155f80
make import relative
jlopezpena Oct 10, 2023
b4a939f
More relative imports
jlopezpena Oct 10, 2023
7d39143
refactor
jlopezpena Oct 10, 2023
e318d35
make all vendored imports relative
jlopezpena Oct 10, 2023
ade65b7
Revert "make all vendored imports relative"
jlopezpena Oct 10, 2023
6db19b9
make all vendored imports relative
jlopezpena Oct 11, 2023
cfa2d55
cleaner relative imports
jlopezpena Oct 11, 2023
981cfe1
more fixes to relative paths
jlopezpena Oct 11, 2023
709fd21
docbuild
jlopezpena Oct 11, 2023
43e5b8f
typing for mypy
jlopezpena Oct 11, 2023
f33f5e4
missing import
jlopezpena Oct 11, 2023
9ad1a6e
typing for helper function
jlopezpena Oct 11, 2023
eb2ac3e
unused imports
jlopezpena Oct 11, 2023
4c2cafe
add path to tutorial notebook for CI
jlopezpena Oct 11, 2023
6ed9fa7
mypy errors
jlopezpena Oct 11, 2023
bd81d49
Ignore typing.py from coverage
jlopezpena Oct 11, 2023
8046828
isort fixes
jlopezpena Oct 11, 2023
17b8ba4
isort issues in helper scripts
jlopezpena Oct 11, 2023
8b4da00
isort issues on tests
jlopezpena Oct 11, 2023
2bd8d25
ignore vendored in black checks
jlopezpena Oct 11, 2023
6f49e20
black fix
jlopezpena Oct 11, 2023
c59b725
flake8 fixes
jlopezpena Oct 11, 2023
42008af
ignore import location in docs conf
jlopezpena Oct 11, 2023
382fef3
trailing comma
jlopezpena Oct 11, 2023
5302c30
Add typing extensions dep for python < 3.10
jlopezpena Oct 11, 2023
39cef95
Merge remote-tracking branch 'origin/master' into antlr411
jlopezpena Oct 11, 2023
3f81322
Add comment about type alias
jlopezpena Nov 21, 2023
ebb8ae2
Recursively add vendored packages to install
jlopezpena Nov 22, 2023
2d049a2
reformat
jlopezpena Nov 22, 2023
d21a1df
we don't want __pycache__ listed as a package...
jlopezpena Nov 22, 2023
ce28524
linting
jlopezpena Nov 22, 2023
e46f93a
typing tripping python 3.8
jlopezpena Nov 22, 2023
919dd9e
reformatting again
jlopezpena Nov 22, 2023
9dae39e
Dont break things on Windows
jlopezpena Nov 29, 2023
1691e64
bump dev version -> 2.4.0.dev2
Jasha10 Feb 15, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ omit =
*tests*
docs/*
omegaconf/grammar/gen/*
omegaconf/vendor/*
omegaconf/version.py
omegaconf/typing.py
.stubs

[report]
Expand Down
2 changes: 1 addition & 1 deletion .flake8
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[flake8]
exclude = .git,.nox,.tox,omegaconf/grammar/gen,build
exclude = .git,.nox,.tox,omegaconf/grammar/gen,build,omegaconf/vendor
max-line-length = 119
select = E,F,W,C
ignore=W503,E203
2 changes: 1 addition & 1 deletion .isort.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@ line_length=88
ensure_newline_before_comments=True
known_third_party=attr,pytest
known_first_party=omegaconf
skip=.eggs,.nox,omegaconf/grammar/gen,build
skip=.eggs,.nox,omegaconf/grammar/gen,build,omegaconf/vendor
Binary file added build_helpers/bin/antlr-4.11.1-complete.jar
Binary file not shown.
Binary file removed build_helpers/bin/antlr-4.9.3-complete.jar
Binary file not shown.
35 changes: 34 additions & 1 deletion build_helpers/build_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import shutil
import subprocess
import sys
from functools import partial
from pathlib import Path
from typing import List, Optional

Expand All @@ -30,7 +31,7 @@ def run(self) -> None:
command = [
"java",
"-jar",
str(build_dir / "bin" / "antlr-4.9.3-complete.jar"),
str(build_dir / "bin" / "antlr-4.11.1-complete.jar"),
"-Dlanguage=Python3",
"-o",
str(project_root / "omegaconf" / "grammar" / "gen"),
Expand All @@ -46,12 +47,44 @@ def run(self) -> None:

subprocess.check_call(command)

self.announce(
"Fixing imports for generated parsers",
level=distutils.log.INFO,
)
self._fix_imports()

def initialize_options(self) -> None:
pass

def finalize_options(self) -> None:
pass

def _fix_imports(self) -> None:
"""Fix imports from the generated parsers to use the vendored antlr4 instead"""
build_dir = Path(__file__).parent.absolute()
project_root = build_dir.parent
lib = "antlr4"
pkgname = 'omegaconf.vendor'

replacements = [
partial( # import antlr4 -> import omegaconf.vendor.antlr4
re.compile(r'(^\s*)import {}\n'.format(lib), flags=re.M).sub,
r'\1from {} import {}\n'.format(pkgname, lib)
),
partial( # from antlr4 -> from fomegaconf.vendor.antlr4
re.compile(r'(^\s*)from {}(\.|\s+)'.format(lib), flags=re.M).sub,
r'\1from {}.{}\2'.format(pkgname, lib)
),
]

path = project_root / "omegaconf" / "grammar" / "gen"
for item in path.iterdir():
if item.is_file() and item.name.endswith(".py"):
text = item.read_text('utf8')
for replacement in replacements:
text = replacement(text)
item.write_text(text, 'utf8')


class BuildPyCommand(build_py.build_py): # pragma: no cover
def run(self) -> None:
Expand Down
121 changes: 121 additions & 0 deletions build_helpers/get_vendored.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import re
import shutil
import subprocess
from functools import partial
from itertools import chain
from pathlib import Path
from typing import Callable, FrozenSet, Generator, List, Set, Tuple, Union

WHITELIST = {'README.txt', '__init__.py', 'vendor.txt'}


def delete_all(*paths: Path, whitelist: Union[Set[str], FrozenSet[str]] = frozenset()) -> None:
"""Clear all the items in each of the indicated paths, except for elements listed
in the whitelist"""
for item in paths:
if item.is_dir():
shutil.rmtree(item, ignore_errors=True)
elif item.is_file() and item.name not in whitelist:
item.unlink()


def iter_subtree(path: Path, depth: int = 0) -> Generator[Tuple[Path, int], None, None]:
"""Recursively yield all files in a subtree, depth-first"""
if not path.is_dir():
if path.is_file():
yield path, depth
return
for item in path.iterdir():
if item.is_dir():
yield from iter_subtree(item, depth + 1)
elif item.is_file():
yield item, depth + 1


def patch_vendor_imports(file: Path, replacements: List[Callable[[str], str]]) -> None:
"""Apply a list of replacements/patches to a given file"""
text = file.read_text('utf8')
for replacement in replacements:
text = replacement(text)
file.write_text(text, 'utf8')


def find_vendored_libs(vendor_dir: Path, whitelist: Set[str]) -> Tuple[List[str], List[Path]]:
vendored_libs = []
paths = []
for item in vendor_dir.iterdir():
if item.is_dir():
vendored_libs.append(item.name)
elif item.is_file() and item.name not in whitelist:
vendored_libs.append(item.stem) # without extension
else: # not a dir or a file not in the whilelist
continue
paths.append(item)
return vendored_libs, paths


def vendor(vendor_dir: Path, relative_imports: bool = False) -> None:
# target package is <parent>.<vendor_dir>; foo/vendor -> foo.vendor
pkgname = f'{vendor_dir.parent.name}.{vendor_dir.name}'

# remove everything
delete_all(*vendor_dir.iterdir(), whitelist=WHITELIST)

# install with pip
subprocess.run([
'pip', 'install', '-t', str(vendor_dir),
'-r', str(vendor_dir / 'vendor.txt'),
'--no-compile', '--no-deps'
])

# delete stuff that's not needed
delete_all(
*vendor_dir.glob('*.dist-info'),
*vendor_dir.glob('*.egg-info'),
vendor_dir / 'bin')

vendored_libs, paths = find_vendored_libs(vendor_dir, WHITELIST)

if not relative_imports:
replacements: List[Callable[[str], str]] = []
for lib in vendored_libs:
replacements += (
partial( # import bar -> import foo.vendor.bar
re.compile(r'(^\s*)import {}\n'.format(lib), flags=re.M).sub,
r'\1from {} import {}\n'.format(pkgname, lib)
),
partial( # from bar -> from foo.vendor.bar
re.compile(r'(^\s*)from {}(\.|\s+)'.format(lib), flags=re.M).sub,
r'\1from {}.{}\2'.format(pkgname, lib)
),
)

for file, depth in chain.from_iterable(map(iter_subtree, paths)):
if relative_imports:
pkgname = '.' * (depth - 1)
replacements = []
for lib in vendored_libs:
replacements += (
partial(
re.compile(r'(^\s*)import {}\n'.format(lib), flags=re.M).sub,
r'\1from {} import {}\n'.format(pkgname, "")
),
partial(
re.compile(r'^from {}(\s+)'.format(lib), flags=re.M).sub,
r'from .{}\1'.format(pkgname)
),
partial(
re.compile(r'(^\s*)from {}(\.+)'.format(lib), flags=re.M).sub,
r'\1from {}\2'.format(pkgname)
),
)
patch_vendor_imports(file, replacements)


if __name__ == '__main__':
# this assumes this is a script in `build_helpers`
here = Path('__file__').resolve().parent
vendor_dir = here / 'omegaconf' / 'vendor'
assert (vendor_dir / 'vendor.txt').exists(), 'omegaconf/vendor/vendor.txt file not found'
assert (vendor_dir / '__init__.py').exists(), 'omegaconf/vendor/__init__.py file not found'
vendor(vendor_dir, relative_imports=True)
Loading
Loading