Skip to content

Commit

Permalink
Merge a8eceab into dcb0b8c
Browse files Browse the repository at this point in the history
  • Loading branch information
LeboyX committed Jan 24, 2019
2 parents dcb0b8c + a8eceab commit b958dfe
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 12 deletions.
14 changes: 14 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,20 @@ Apart from passing options on the command line it's also possible to add a dedic
# Dump lots of logging info to antlr-<timestamp>.log (yes|no); default: no
#x-log = no
Some flags specific to setuptools-antlr can also be provided to the ``[antlr]``
section of your ``setup.cfg``.

.. code:: ini
[antlr]
# Configure the root directory for your module; default: .
#package-dir = .
# Control the creation of __init__.py files in all directories about output
# directories; default: yes
#gen-packages = yes
A reference configuration is provided in the ``resources`` directory.

Sample
Expand Down
7 changes: 7 additions & 0 deletions resources/setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,10 @@
#x-force-atn = no
# Dump lots of logging info to antlr-<timestamp>.log (yes|no); default: no
#x-log = no

# Configure the root directory for your module; default: .
#package-dir = .

# Control the creation of __init__.py files in all directories about output
# directories; default: yes
#gen-packages = yes
38 changes: 27 additions & 11 deletions setuptools_antlr/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,14 @@ class AntlrCommand(setuptools.Command):
('x-dbg-st-wait', None, 'wait for STViz to close before continuing'),
('x-exact-output-dir', None, 'output goes into -o directories regardless of paths/package'),
('x-force-atn', None, 'use the ATN simulator for all predictions'),
('x-log', None, 'dump lots of logging info to antlr-<timestamp>.log')
('x-log', None, 'dump lots of logging info to antlr-<timestamp>.log'),
('package-dir', 'd', 'root directory for your source content'),
('gen-packages', None, 'configure whether __init__.py files will be generated for your project'),
]

boolean_options = ['atn', 'long-messages', 'listener', 'no-listener', 'visitor', 'no-visitor',
'depend', 'w-error', 'x-dbg-st', 'x-dbg-st-wait', 'x-exact-output-dir',
'x-force-atn', 'x-log']
'x-force-atn', 'x-log', 'gen-packages']

negative_opt = {'no-listener': 'listener', 'no-visitor': 'visitor'}

Expand All @@ -163,6 +165,9 @@ def initialize_options(self):
self.x_force_atn = 0
self.x_log = 0

self.package_dir = '.'
self.gen_packages = 1

def finalize_options(self):
"""Sets final values for all the options that this command supports. This is always called
as late as possible, ie. after any option assignments from the command-line or from other
Expand Down Expand Up @@ -305,6 +310,25 @@ def _create_init_file(cls, path: pathlib.Path) -> bool:
return False
return True

def _create_package_init_files(self, grammar_package_dir: pathlib.Path):
"""Create Python package including parent packages if don't exist
:param grammar_package_dir: path where grammar was generated
"""
distutils.log.info('generating __init__.py for {}'.format(grammar_package_dir.name))
self._create_init_file(grammar_package_dir)

# Go up directory hierarchy and make all parents packages, if requested
if self.gen_packages:
base_dir = pathlib.Path(self.package_dir).resolve()
parent_dir = grammar_package_dir.resolve().parent

while base_dir < parent_dir:
distutils.log.info('generating __init__.py for {}'.format(parent_dir.name))
self._create_init_file(parent_dir)
parent_dir = parent_dir.parent
pass

def run(self):
"""Performs all tasks necessary to generate ANTLR based parsers for all found grammars. This
process is controlled by the user options passed on the command line or set internally to
Expand Down Expand Up @@ -397,15 +421,7 @@ def run(self):
else:
distutils.log.info('generating {} parser -> {}'.format(grammar.name, package_dir))

# create Python package including parent packages if don't exist
self._create_init_file(package_dir)

base_dir = pathlib.Path('.').resolve()
parent_dir = package_dir.resolve().parent

while base_dir < parent_dir:
self._create_init_file(parent_dir)
parent_dir = parent_dir.parent
self._create_package_init_files(package_dir)

# call ANTLR for parser generation
result = subprocess.run(run_args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
Expand Down
75 changes: 74 additions & 1 deletion tests/test_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,77 @@ def test_create_init_file_exists(self, tmpdir, command):
assert not created
assert init_file.stat().st_mtime_ns == origin_init_mtime_ns

@unittest.mock.patch('setuptools_antlr.command.find_java')
@unittest.mock.patch.object(AntlrCommand, '_find_antlr')
@unittest.mock.patch('subprocess.run')
@unittest.mock.patch.object(AntlrCommand, '_find_grammars')
def test_create_package_init_files_no_gen(self, mock_find_grammars, mock_run, mock_find_antlr,
mock_find_java, tmpdir, command):
# Boilerplate to init command to run successfully
java_exe = pathlib.Path('c:/path/to/java/bin/java.exe')
antlr_jar = pathlib.Path('antlr-4.5.3-complete.jar')
mock_find_java.return_value = java_exe
mock_find_antlr.return_value = antlr_jar

grammar = AntlrGrammar(pathlib.Path('SomeGrammar.g4'))
mock_find_grammars.return_value = [grammar]
mock_run.return_value = unittest.mock.Mock(returncode=0)

# Setup grammar generation directories
base_dir = str(tmpdir.mkdir('base'))
parent_dir = pathlib.Path(base_dir, 'parent')
grammar_dir = pathlib.Path(parent_dir, 'some_grammar')

# Configure command to not generate packages b/c the grammars' directory
command.output['default'] = parent_dir
command.gen_packages = 0
os.chdir(str(base_dir))

command.run()

# We should have a package __init__.py, but not a parent one
package_init_file = pathlib.Path(grammar_dir, '__init__.py')
parent_init_file = pathlib.Path(parent_dir, '__init__.py')
assert package_init_file.exists()
assert not parent_init_file.exists()

@unittest.mock.patch('setuptools_antlr.command.find_java')
@unittest.mock.patch.object(AntlrCommand, '_find_antlr')
@unittest.mock.patch('subprocess.run')
@unittest.mock.patch.object(AntlrCommand, '_find_grammars')
def test_create_package_init_files_package_dir(self, mock_find_grammars, mock_run, mock_find_antlr,
mock_find_java, tmpdir, command):
# Boilerplate to init command to run successfully
java_exe = pathlib.Path('c:/path/to/java/bin/java.exe')
antlr_jar = pathlib.Path('antlr-4.5.3-complete.jar')
mock_find_java.return_value = java_exe
mock_find_antlr.return_value = antlr_jar

grammar = AntlrGrammar(pathlib.Path('SomeGrammar.g4'))
mock_find_grammars.return_value = [grammar]
mock_run.return_value = unittest.mock.Mock(returncode=0)

# Setup grammar generation directories
base_dir = str(tmpdir.mkdir('base'))
parent_dir = pathlib.Path(base_dir, 'parent')
grammar_dir = pathlib.Path(parent_dir, 'some_grammar')

#
# Configure command to consider the parent_dir to be the base_dir,
# meaning it won't generate an __init__.py file for it
#
command.output['default'] = parent_dir
command.package_dir = parent_dir.name
os.chdir(str(base_dir))

command.run()

# We should have a package __init__.py, but not a parent one
package_init_file = pathlib.Path(grammar_dir, '__init__.py')
parent_init_file = pathlib.Path(parent_dir, '__init__.py')
assert package_init_file.exists()
assert not parent_init_file.exists()

def test_finalize_options_default(self, command):
command.finalize_options()

Expand All @@ -193,6 +264,8 @@ def test_finalize_options_default(self, command):
assert command.x_dbg_st_wait == 0
assert command.x_force_atn == 0
assert command.x_log == 0
assert command.package_dir == '.'
assert command.gen_packages == 1

def test_finalize_options_grammars_single(self, command):
command.grammars = 'Foo'
Expand Down Expand Up @@ -878,4 +951,4 @@ def test_run_multiple_library_location(self, mock_find_grammars, mock_run, mock_
with pytest.raises(distutils.errors.DistutilsOptionError) as excinfo:
command.run()
assert excinfo.match('Imported grammars of \'SomeGrammar\' are located in more than one '
'directory.')
'directory.')

0 comments on commit b958dfe

Please sign in to comment.