Hi all, So cumin is failing to build in Debian because it does some nasty things during build (namely using $HOME, because of that setup.py develop --user).
I have a beginning of a fix for this in the bugfix-1026005 branch on salsa. The problem now is that it fails on *other* parts of the test suite, which wasn't failing before. I suspect it might be because the code layout is different, but I can't figure out how or why... Riccardo, Moritz, any ideas on how this could be fixed? Here's the failing build log: anarcat@angela:cumin$ dpkg-buildpackage dpkg-buildpackage: info: source package cumin dpkg-buildpackage: info: source version 4.1.1-3 dpkg-buildpackage: info: source distribution unstable dpkg-buildpackage: info: source changed by Antoine Beaupré <anar...@debian.org> dpkg-buildpackage: info: host architecture amd64 dpkg-source --before-build . dpkg-source: info: using patch list from debian/patches/series dpkg-source: info: applying 0001-import-version-number-from-setuptools_scm.patch fakeroot debian/rules clean dh clean --with python3 --buildsystem=pybuild dh_auto_clean -O--buildsystem=pybuild I: pybuild base:240: python3.10 setup.py clean running clean removing '/home/anarcat/dist/cumin/.pybuild/cpython3_3.10_cumin/build' (and everything under it) 'build/bdist.linux-x86_64' does not exist -- can't clean it 'build/scripts-3.10' does not exist -- can't clean it dh_autoreconf_clean -O--buildsystem=pybuild dh_clean -O--buildsystem=pybuild dpkg-source -b . dpkg-source: info: using source format '3.0 (quilt)' dpkg-source: warning: upstream signing key but no upstream tarball signature dpkg-source: info: building cumin using existing ./cumin_4.1.1.orig.tar.gz dpkg-source: info: using patch list from debian/patches/series dpkg-source: info: building cumin in cumin_4.1.1-3.debian.tar.xz dpkg-source: info: building cumin in cumin_4.1.1-3.dsc debian/rules build dh build --with python3 --buildsystem=pybuild dh_update_autotools_config -O--buildsystem=pybuild dh_autoreconf -O--buildsystem=pybuild dh_auto_configure -O--buildsystem=pybuild I: pybuild base:240: python3.10 setup.py config running config dh_auto_build -O--buildsystem=pybuild I: pybuild base:240: /usr/bin/python3 setup.py build running build running build_py creating /home/anarcat/dist/cumin/.pybuild/cpython3_3.10_cumin/build/cumin copying cumin/transport.py -> /home/anarcat/dist/cumin/.pybuild/cpython3_3.10_cumin/build/cumin copying cumin/color.py -> /home/anarcat/dist/cumin/.pybuild/cpython3_3.10_cumin/build/cumin copying cumin/cli.py -> /home/anarcat/dist/cumin/.pybuild/cpython3_3.10_cumin/build/cumin copying cumin/__init__.py -> /home/anarcat/dist/cumin/.pybuild/cpython3_3.10_cumin/build/cumin copying cumin/grammar.py -> /home/anarcat/dist/cumin/.pybuild/cpython3_3.10_cumin/build/cumin copying cumin/query.py -> /home/anarcat/dist/cumin/.pybuild/cpython3_3.10_cumin/build/cumin creating /home/anarcat/dist/cumin/.pybuild/cpython3_3.10_cumin/build/cumin/backends copying cumin/backends/direct.py -> /home/anarcat/dist/cumin/.pybuild/cpython3_3.10_cumin/build/cumin/backends copying cumin/backends/openstack.py -> /home/anarcat/dist/cumin/.pybuild/cpython3_3.10_cumin/build/cumin/backends copying cumin/backends/puppetdb.py -> /home/anarcat/dist/cumin/.pybuild/cpython3_3.10_cumin/build/cumin/backends copying cumin/backends/knownhosts.py -> /home/anarcat/dist/cumin/.pybuild/cpython3_3.10_cumin/build/cumin/backends copying cumin/backends/__init__.py -> /home/anarcat/dist/cumin/.pybuild/cpython3_3.10_cumin/build/cumin/backends creating /home/anarcat/dist/cumin/.pybuild/cpython3_3.10_cumin/build/cumin/transports copying cumin/transports/clustershell.py -> /home/anarcat/dist/cumin/.pybuild/cpython3_3.10_cumin/build/cumin/transports copying cumin/transports/__init__.py -> /home/anarcat/dist/cumin/.pybuild/cpython3_3.10_cumin/build/cumin/transports dh_auto_test -O--buildsystem=pybuild I: pybuild base:240: cd /home/anarcat/dist/cumin/.pybuild/cpython3_3.10_cumin/build; python3.10 -m pytest /home/anarcat/dist/cumin/cumin/tests/unit ==================================================================== test session starts ==================================================================== platform linux -- Python 3.10.8, pytest-7.1.2, pluggy-1.0.0+repack rootdir: /home/anarcat/dist/cumin, configfile: pytest.ini plugins: arraydiff-0.5.0, betamax-0.8.1, astropy-header-0.2.2, remotedata-0.3.3, requests-mock-1.9.3, hypothesis-6.36.0, openfiles-0.5.0, mock-3.8.2, astropy-0.10.0, cov-4.0.0, doctestplus-0.12.1, filter-subpackage-0.1.1 collected 416 items ../../../cumin/tests/unit/test_backends.py . [ 0%] ../../../cumin/tests/unit/test_cli.py .............................. [ 7%] ../../../cumin/tests/unit/test_color.py ......... [ 9%] ../../../cumin/tests/unit/test_grammar.py F.......... [ 12%] ../../../cumin/tests/unit/test_init.py ............................. [ 19%] ../../../cumin/tests/unit/test_query.py ...F......F....F [ 23%] ../../../cumin/tests/unit/test_transport.py .... [ 24%] ../../../cumin/tests/unit/backends/test_direct.py ..F [ 24%] ../../../cumin/tests/unit/backends/test_grammars.py FF...... [ 26%] ../../../cumin/tests/unit/backends/test_knownhosts.py ......F.F................. [ 32%] ../../../cumin/tests/unit/backends/test_openstack.py ........... [ 35%] ../../../cumin/tests/unit/backends/test_puppetdb.py ............................................................................................... [ 58%] ../../../cumin/tests/unit/transports/test_clustershell.py ............................................. [ 69%] ../../../cumin/tests/unit/transports/test_init.py ................................................................................................... [ 93%] ............................. [100%] ========================================================================= FAILURES ========================================================================== ____________________________________________________________________ test_valid_strings _____________________________________________________________________ def test_valid_strings(): """Run quick pyparsing test over valid grammar strings.""" results = grammar.grammar(REGISTERED_BACKENDS.keys()).runTests( get_fixture(os.path.join('grammar', 'valid_grammars.txt'), as_string=True)) > assert results[0] E assert False ../../../cumin/tests/unit/test_grammar.py:22: AssertionError ------------------------------------------------------------------- Captured stdout call -------------------------------------------------------------------- # Valid grammars A:alias_name [['A:alias_name']] [0]: ['A:alias_name'] - alias: 'alias_name' A:alias-name [['A:alias-name']] [0]: ['A:alias-name'] - alias: 'alias-name' A:alias.name [['A:alias.name']] [0]: ['A:alias.name'] - alias: 'alias.name' A:alias+name [['A:alias+name']] [0]: ['A:alias+name'] - alias: 'alias+name' P{puppetdb query (with subgroups) "and even P{}}, if quoted"} [['P{puppetdb query (with subgroups) "and even P{}}, if quoted"}']] [0]: ['P{puppetdb query (with subgroups) "and even P{}}, if quoted"}'] - backend: 'P' - query: 'puppetdb query (with subgroups) "and even P{}}, if quoted"' D{direct (query) with and or ((subgroups[10-20]))} [['D{direct (query) with and or ((subgroups[10-20]))}']] [0]: ['D{direct (query) with and or ((subgroups[10-20]))}'] - backend: 'D' - query: 'direct (query) with and or ((subgroups[10-20]))' P{query1} and D{query2} [['P{query1}'], ['and', 'D{query2}']] [0]: ['P{query1}'] - backend: 'P' - query: 'query1' [1]: ['and', 'D{query2}'] - backend: 'D' - bool: 'and' - query: 'query2' P{query1} and not D{query2} P{query1} and not D{query2} ^ ParseException: Expected end of text, found 'and' (at char 10), (line:1, col:11) FAIL: Expected end of text, found 'and' (at char 10), (line:1, col:11) P{query1} or D{query1} [['P{query1}'], ['or', 'D{query1}']] [0]: ['P{query1}'] - backend: 'P' - query: 'query1' [1]: ['or', 'D{query1}'] - backend: 'D' - bool: 'or' - query: 'query1' P{query1} xor D{query1} [['P{query1}'], ['xor', 'D{query1}']] [0]: ['P{query1}'] - backend: 'P' - query: 'query1' [1]: ['xor', 'D{query1}'] - backend: 'D' - bool: 'xor' - query: 'query1' P{query} and A:alias [['P{query}'], ['and', 'A:alias']] [0]: ['P{query}'] - backend: 'P' - query: 'query' [1]: ['and', 'A:alias'] - alias: 'alias' - bool: 'and' P{query} and not A:alias P{query} and not A:alias ^ ParseException: Expected end of text, found 'and' (at char 9), (line:1, col:10) FAIL: Expected end of text, found 'and' (at char 9), (line:1, col:10) P{query} or A:alias [['P{query}'], ['or', 'A:alias']] [0]: ['P{query}'] - backend: 'P' - query: 'query' [1]: ['or', 'A:alias'] - alias: 'alias' - bool: 'or' P{query} xor A:alias [['P{query}'], ['xor', 'A:alias']] [0]: ['P{query}'] - backend: 'P' - query: 'query' [1]: ['xor', 'A:alias'] - alias: 'alias' - bool: 'xor' A:alias and P{query} [['A:alias'], ['and', 'P{query}']] [0]: ['A:alias'] - alias: 'alias' [1]: ['and', 'P{query}'] - backend: 'P' - bool: 'and' - query: 'query' A:alias and not P{query} A:alias and not P{query} ^ ParseException: Expected end of text, found 'and' (at char 8), (line:1, col:9) FAIL: Expected end of text, found 'and' (at char 8), (line:1, col:9) A:alias or P{query} [['A:alias'], ['or', 'P{query}']] [0]: ['A:alias'] - alias: 'alias' [1]: ['or', 'P{query}'] - backend: 'P' - bool: 'or' - query: 'query' A:alias xor P{query} [['A:alias'], ['xor', 'P{query}']] [0]: ['A:alias'] - alias: 'alias' [1]: ['xor', 'P{query}'] - backend: 'P' - bool: 'xor' - query: 'query' A:alias1 and (P{query1} or D{query2}) and not A:alias2 xor D{query3} A:alias1 and (P{query1} or D{query2}) and not A:alias2 xor D{query3} ^ ParseException: Expected end of text, found 'and' (at char 38), (line:1, col:39) FAIL: Expected end of text, found 'and' (at char 38), (line:1, col:39) A:alias1 and ((P{query1} or D{query2}) and not A:alias2) xor D{query3} A:alias1 and ((P{query1} or D{query2}) and not A:alias2) xor D{query3} ^ ParseException: Expected end of text, found 'and' (at char 9), (line:1, col:10) FAIL: Expected end of text, found 'and' (at char 9), (line:1, col:10) ________________________________________________________________ test_execute_global_and_not ________________________________________________________________ self = <cumin.query.Query object at 0x7fda656e3c70>, query_string = 'D{host[1-5]} and not D{host2}' def execute(self, query_string): """Override parent class execute method to implement the multi-query capability. :Parameters: according to parent :py:meth:`cumin.backends.BaseQueryAggregator.execute`. Returns: ClusterShell.NodeSet.NodeSet: with the FQDNs of the matching hosts. Raises: cumin.backends.InvalidQueryError: if unable to parse the query. """ if 'default_backend' not in self.config: try: # No default backend set, using directly the global grammar > return super().execute(query_string) ../../../cumin/query.py:54: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <cumin.query.Query object at 0x7fda656e3c70>, query_string = 'D{host[1-5]} and not D{host2}' def execute(self, query_string): """Build and execute the query, return the NodeSet of FQDN hostnames that matches. Arguments: query_string (str): the query string to be parsed and executed. Returns: ClusterShell.NodeSet.NodeSet: with the FQDNs of the matching hosts. """ > self._build(query_string) ../../../cumin/backends/__init__.py:47: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <cumin.query.Query object at 0x7fda656e3c70>, query_string = 'D{host[1-5]} and not D{host2}' def _build(self, query_string): """Override parent method to reset the stack and log it. :Parameters: according to parent :py:meth:`cumin.backends.BaseQuery._build`. """ self.stack = self._get_stack_element() self.stack_pointer = self.stack > super()._build(query_string) ../../../cumin/backends/__init__.py:111: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <cumin.query.Query object at 0x7fda656e3c70>, query_string = 'D{host[1-5]} and not D{host2}' def _build(self, query_string): """Parse the query string according to the grammar and build the query for later execution. Arguments: query_string (str): the query string to be parsed. """ self.logger.trace('Parsing query: %s', query_string) > parsed = self.grammar.parseString(query_string.strip(), parseAll=True) ../../../cumin/backends/__init__.py:76: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = Forward: {Group:({{Combine:({Combine:({D | K | O | P '{'}) SkipTo:('}') '}'}) | Combine:({'A' ':' W:(+-.0-9A-Z_a-z)})}...e:({Combine:({D | K | O | P '{'}) SkipTo:('}') '}'}) | Combine:({'A' ':' W:(+-.0-9A-Z_a-z)})} | {{'(' : ...} ')'})]...} instring = 'D{host[1-5]} and not D{host2}', parse_all = False def parse_string( self, instring: str, parse_all: bool = False, *, parseAll: bool = False ) -> ParseResults: """ Parse a string with respect to the parser definition. This function is intended as the primary interface to the client code. :param instring: The input string to be parsed. :param parse_all: If set, the entire input string must match the grammar. :param parseAll: retained for pre-PEP8 compatibility, will be removed in a future release. :raises ParseException: Raised if ``parse_all`` is set and the input string does not match the whole grammar. :returns: the parsed data as a :class:`ParseResults` object, which may be accessed as a `list`, a `dict`, or an object with attributes if the given parser includes results names. If the input string is required to match the entire grammar, ``parse_all`` flag must be set to ``True``. This is also equivalent to ending the grammar with :class:`StringEnd`(). To report proper column numbers, ``parse_string`` operates on a copy of the input string where all tabs are converted to spaces (8 spaces per tab, as per the default in ``string.expandtabs``). If the input string contains tabs and the grammar uses parse actions that use the ``loc`` argument to index into the string being parsed, one can ensure a consistent view of the input string by doing one of the following: - calling ``parse_with_tabs`` on your grammar before calling ``parse_string`` (see :class:`parse_with_tabs`), - define your parse action using the full ``(s,loc,toks)`` signature, and reference the input string using the parse action's ``s`` argument, or - explicitly expand the tabs in your input string before calling ``parse_string``. Examples: By default, partial matches are OK. >>> res = Word('a').parse_string('aaaaabaaa') >>> print(res) ['aaaaa'] The parsing behavior varies by the inheriting class of this abstract class. Please refer to the children directly to see more examples. It raises an exception if parse_all flag is set and instring does not match the whole grammar. >>> res = Word('a').parse_string('aaaaabaaa', parse_all=True) Traceback (most recent call last): ... pyparsing.ParseException: Expected end of text, found 'b' (at char 5), (line:1, col:6) """ parseAll = parse_all or parseAll ParserElement.reset_cache() if not self.streamlined: self.streamline() for e in self.ignoreExprs: e.streamline() if not self.keepTabs: instring = instring.expandtabs() try: loc, tokens = self._parse(instring, 0) if parseAll: loc = self.preParse(instring, loc) se = Empty() + StringEnd() se._parse(instring, loc) except ParseBaseException as exc: if ParserElement.verbose_stacktrace: raise else: # catch and re-raise exception from here, clearing out pyparsing internal stack trace > raise exc.with_traceback(None) E pyparsing.exceptions.ParseException: Expected end of text, found 'and' (at char 13), (line:1, col:14) /usr/lib/python3/dist-packages/pyparsing/core.py:1134: ParseException During handling of the above exception, another exception occurred: def test_execute_global_and_not(): """Executing an 'and not' between two queries should return the difference of the hosts.""" query = Query({}) > hosts = query.execute('D{host[1-5]} and not D{host2}') ../../../cumin/tests/unit/test_query.py:34: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <cumin.query.Query object at 0x7fda656e3c70>, query_string = 'D{host[1-5]} and not D{host2}' def execute(self, query_string): """Override parent class execute method to implement the multi-query capability. :Parameters: according to parent :py:meth:`cumin.backends.BaseQueryAggregator.execute`. Returns: ClusterShell.NodeSet.NodeSet: with the FQDNs of the matching hosts. Raises: cumin.backends.InvalidQueryError: if unable to parse the query. """ if 'default_backend' not in self.config: try: # No default backend set, using directly the global grammar return super().execute(query_string) except ParseException as e: > raise InvalidQueryError(("Unable to parse the query '{query}' > with the global grammar and no " "default backend is set:\n{error}").format(query=query_string, error=e)) E cumin.backends.InvalidQueryError: Unable to parse the query 'D{host[1-5]} and not D{host2}' with the global grammar and no default backend is set: E Expected end of text, found 'and' (at char 13), (line:1, col:14) ../../../cumin/query.py:56: InvalidQueryError __________________________________________________________________ test_execute_subgroups ___________________________________________________________________ self = <cumin.query.Query object at 0x7fda656ae9b0>, query_string = '(D{host1} or D{host2}) and not (D{host1})' def execute(self, query_string): """Override parent class execute method to implement the multi-query capability. :Parameters: according to parent :py:meth:`cumin.backends.BaseQueryAggregator.execute`. Returns: ClusterShell.NodeSet.NodeSet: with the FQDNs of the matching hosts. Raises: cumin.backends.InvalidQueryError: if unable to parse the query. """ if 'default_backend' not in self.config: try: # No default backend set, using directly the global grammar > return super().execute(query_string) ../../../cumin/query.py:54: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <cumin.query.Query object at 0x7fda656ae9b0>, query_string = '(D{host1} or D{host2}) and not (D{host1})' def execute(self, query_string): """Build and execute the query, return the NodeSet of FQDN hostnames that matches. Arguments: query_string (str): the query string to be parsed and executed. Returns: ClusterShell.NodeSet.NodeSet: with the FQDNs of the matching hosts. """ > self._build(query_string) ../../../cumin/backends/__init__.py:47: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <cumin.query.Query object at 0x7fda656ae9b0>, query_string = '(D{host1} or D{host2}) and not (D{host1})' def _build(self, query_string): """Override parent method to reset the stack and log it. :Parameters: according to parent :py:meth:`cumin.backends.BaseQuery._build`. """ self.stack = self._get_stack_element() self.stack_pointer = self.stack > super()._build(query_string) ../../../cumin/backends/__init__.py:111: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <cumin.query.Query object at 0x7fda656ae9b0>, query_string = '(D{host1} or D{host2}) and not (D{host1})' def _build(self, query_string): """Parse the query string according to the grammar and build the query for later execution. Arguments: query_string (str): the query string to be parsed. """ self.logger.trace('Parsing query: %s', query_string) > parsed = self.grammar.parseString(query_string.strip(), parseAll=True) ../../../cumin/backends/__init__.py:76: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = Forward: {Group:({{Combine:({Combine:({D | K | O | P '{'}) SkipTo:('}') '}'}) | Combine:({'A' ':' W:(+-.0-9A-Z_a-z)})}...e:({Combine:({D | K | O | P '{'}) SkipTo:('}') '}'}) | Combine:({'A' ':' W:(+-.0-9A-Z_a-z)})} | {{'(' : ...} ')'})]...} instring = '(D{host1} or D{host2}) and not (D{host1})', parse_all = False def parse_string( self, instring: str, parse_all: bool = False, *, parseAll: bool = False ) -> ParseResults: """ Parse a string with respect to the parser definition. This function is intended as the primary interface to the client code. :param instring: The input string to be parsed. :param parse_all: If set, the entire input string must match the grammar. :param parseAll: retained for pre-PEP8 compatibility, will be removed in a future release. :raises ParseException: Raised if ``parse_all`` is set and the input string does not match the whole grammar. :returns: the parsed data as a :class:`ParseResults` object, which may be accessed as a `list`, a `dict`, or an object with attributes if the given parser includes results names. If the input string is required to match the entire grammar, ``parse_all`` flag must be set to ``True``. This is also equivalent to ending the grammar with :class:`StringEnd`(). To report proper column numbers, ``parse_string`` operates on a copy of the input string where all tabs are converted to spaces (8 spaces per tab, as per the default in ``string.expandtabs``). If the input string contains tabs and the grammar uses parse actions that use the ``loc`` argument to index into the string being parsed, one can ensure a consistent view of the input string by doing one of the following: - calling ``parse_with_tabs`` on your grammar before calling ``parse_string`` (see :class:`parse_with_tabs`), - define your parse action using the full ``(s,loc,toks)`` signature, and reference the input string using the parse action's ``s`` argument, or - explicitly expand the tabs in your input string before calling ``parse_string``. Examples: By default, partial matches are OK. >>> res = Word('a').parse_string('aaaaabaaa') >>> print(res) ['aaaaa'] The parsing behavior varies by the inheriting class of this abstract class. Please refer to the children directly to see more examples. It raises an exception if parse_all flag is set and instring does not match the whole grammar. >>> res = Word('a').parse_string('aaaaabaaa', parse_all=True) Traceback (most recent call last): ... pyparsing.ParseException: Expected end of text, found 'b' (at char 5), (line:1, col:6) """ parseAll = parse_all or parseAll ParserElement.reset_cache() if not self.streamlined: self.streamline() for e in self.ignoreExprs: e.streamline() if not self.keepTabs: instring = instring.expandtabs() try: loc, tokens = self._parse(instring, 0) if parseAll: loc = self.preParse(instring, loc) se = Empty() + StringEnd() se._parse(instring, loc) except ParseBaseException as exc: if ParserElement.verbose_stacktrace: raise else: # catch and re-raise exception from here, clearing out pyparsing internal stack trace > raise exc.with_traceback(None) E pyparsing.exceptions.ParseException: Expected end of text, found 'and' (at char 23), (line:1, col:24) /usr/lib/python3/dist-packages/pyparsing/core.py:1134: ParseException During handling of the above exception, another exception occurred: def test_execute_subgroups(): """Executing a query with multiple subgroups should return the matching hosts.""" query = Query({}) > hosts = query.execute('(D{host1} or D{host2}) and not (D{host1})') ../../../cumin/tests/unit/test_query.py:92: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <cumin.query.Query object at 0x7fda656ae9b0>, query_string = '(D{host1} or D{host2}) and not (D{host1})' def execute(self, query_string): """Override parent class execute method to implement the multi-query capability. :Parameters: according to parent :py:meth:`cumin.backends.BaseQueryAggregator.execute`. Returns: ClusterShell.NodeSet.NodeSet: with the FQDNs of the matching hosts. Raises: cumin.backends.InvalidQueryError: if unable to parse the query. """ if 'default_backend' not in self.config: try: # No default backend set, using directly the global grammar return super().execute(query_string) except ParseException as e: > raise InvalidQueryError(("Unable to parse the query '{query}' > with the global grammar and no " "default backend is set:\n{error}").format(query=query_string, error=e)) E cumin.backends.InvalidQueryError: Unable to parse the query '(D{host1} or D{host2}) and not (D{host1})' with the global grammar and no default backend is set: E Expected end of text, found 'and' (at char 23), (line:1, col:24) ../../../cumin/query.py:56: InvalidQueryError ________________________________________________________________ test_execute_complex_global ________________________________________________________________ self = <cumin.query.Query object at 0x7fda65524f70> query_string = '(D{(host1 or host2) and host[1-5]}) or ((D{host[100-150]} and not D{host1[20-30]}) and D{host1[01,15,30]})' def execute(self, query_string): """Override parent class execute method to implement the multi-query capability. :Parameters: according to parent :py:meth:`cumin.backends.BaseQueryAggregator.execute`. Returns: ClusterShell.NodeSet.NodeSet: with the FQDNs of the matching hosts. Raises: cumin.backends.InvalidQueryError: if unable to parse the query. """ if 'default_backend' not in self.config: try: # No default backend set, using directly the global grammar > return super().execute(query_string) ../../../cumin/query.py:54: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <cumin.query.Query object at 0x7fda65524f70> query_string = '(D{(host1 or host2) and host[1-5]}) or ((D{host[100-150]} and not D{host1[20-30]}) and D{host1[01,15,30]})' def execute(self, query_string): """Build and execute the query, return the NodeSet of FQDN hostnames that matches. Arguments: query_string (str): the query string to be parsed and executed. Returns: ClusterShell.NodeSet.NodeSet: with the FQDNs of the matching hosts. """ > self._build(query_string) ../../../cumin/backends/__init__.py:47: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <cumin.query.Query object at 0x7fda65524f70> query_string = '(D{(host1 or host2) and host[1-5]}) or ((D{host[100-150]} and not D{host1[20-30]}) and D{host1[01,15,30]})' def _build(self, query_string): """Override parent method to reset the stack and log it. :Parameters: according to parent :py:meth:`cumin.backends.BaseQuery._build`. """ self.stack = self._get_stack_element() self.stack_pointer = self.stack > super()._build(query_string) ../../../cumin/backends/__init__.py:111: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <cumin.query.Query object at 0x7fda65524f70> query_string = '(D{(host1 or host2) and host[1-5]}) or ((D{host[100-150]} and not D{host1[20-30]}) and D{host1[01,15,30]})' def _build(self, query_string): """Parse the query string according to the grammar and build the query for later execution. Arguments: query_string (str): the query string to be parsed. """ self.logger.trace('Parsing query: %s', query_string) > parsed = self.grammar.parseString(query_string.strip(), parseAll=True) ../../../cumin/backends/__init__.py:76: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = Forward: {Group:({{Combine:({Combine:({D | K | O | P '{'}) SkipTo:('}') '}'}) | Combine:({'A' ':' W:(+-.0-9A-Z_a-z)})}...e:({Combine:({D | K | O | P '{'}) SkipTo:('}') '}'}) | Combine:({'A' ':' W:(+-.0-9A-Z_a-z)})} | {{'(' : ...} ')'})]...} instring = '(D{(host1 or host2) and host[1-5]}) or ((D{host[100-150]} and not D{host1[20-30]}) and D{host1[01,15,30]})', parse_all = False def parse_string( self, instring: str, parse_all: bool = False, *, parseAll: bool = False ) -> ParseResults: """ Parse a string with respect to the parser definition. This function is intended as the primary interface to the client code. :param instring: The input string to be parsed. :param parse_all: If set, the entire input string must match the grammar. :param parseAll: retained for pre-PEP8 compatibility, will be removed in a future release. :raises ParseException: Raised if ``parse_all`` is set and the input string does not match the whole grammar. :returns: the parsed data as a :class:`ParseResults` object, which may be accessed as a `list`, a `dict`, or an object with attributes if the given parser includes results names. If the input string is required to match the entire grammar, ``parse_all`` flag must be set to ``True``. This is also equivalent to ending the grammar with :class:`StringEnd`(). To report proper column numbers, ``parse_string`` operates on a copy of the input string where all tabs are converted to spaces (8 spaces per tab, as per the default in ``string.expandtabs``). If the input string contains tabs and the grammar uses parse actions that use the ``loc`` argument to index into the string being parsed, one can ensure a consistent view of the input string by doing one of the following: - calling ``parse_with_tabs`` on your grammar before calling ``parse_string`` (see :class:`parse_with_tabs`), - define your parse action using the full ``(s,loc,toks)`` signature, and reference the input string using the parse action's ``s`` argument, or - explicitly expand the tabs in your input string before calling ``parse_string``. Examples: By default, partial matches are OK. >>> res = Word('a').parse_string('aaaaabaaa') >>> print(res) ['aaaaa'] The parsing behavior varies by the inheriting class of this abstract class. Please refer to the children directly to see more examples. It raises an exception if parse_all flag is set and instring does not match the whole grammar. >>> res = Word('a').parse_string('aaaaabaaa', parse_all=True) Traceback (most recent call last): ... pyparsing.ParseException: Expected end of text, found 'b' (at char 5), (line:1, col:6) """ parseAll = parse_all or parseAll ParserElement.reset_cache() if not self.streamlined: self.streamline() for e in self.ignoreExprs: e.streamline() if not self.keepTabs: instring = instring.expandtabs() try: loc, tokens = self._parse(instring, 0) if parseAll: loc = self.preParse(instring, loc) se = Empty() + StringEnd() se._parse(instring, loc) except ParseBaseException as exc: if ParserElement.verbose_stacktrace: raise else: # catch and re-raise exception from here, clearing out pyparsing internal stack trace > raise exc.with_traceback(None) E pyparsing.exceptions.ParseException: Expected end of text, found 'or' (at char 36), (line:1, col:37) /usr/lib/python3/dist-packages/pyparsing/core.py:1134: ParseException During handling of the above exception, another exception occurred: def test_execute_complex_global(): """Executing a valid complex query should return the matching hosts.""" query = Query({}) > hosts = query.execute( '(D{(host1 or host2) and host[1-5]}) or ((D{host[100-150]} and not D{host1[20-30]}) and D{host1[01,15,30]})') ../../../cumin/tests/unit/test_query.py:127: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <cumin.query.Query object at 0x7fda65524f70> query_string = '(D{(host1 or host2) and host[1-5]}) or ((D{host[100-150]} and not D{host1[20-30]}) and D{host1[01,15,30]})' def execute(self, query_string): """Override parent class execute method to implement the multi-query capability. :Parameters: according to parent :py:meth:`cumin.backends.BaseQueryAggregator.execute`. Returns: ClusterShell.NodeSet.NodeSet: with the FQDNs of the matching hosts. Raises: cumin.backends.InvalidQueryError: if unable to parse the query. """ if 'default_backend' not in self.config: try: # No default backend set, using directly the global grammar return super().execute(query_string) except ParseException as e: > raise InvalidQueryError(("Unable to parse the query '{query}' > with the global grammar and no " "default backend is set:\n{error}").format(query=query_string, error=e)) E cumin.backends.InvalidQueryError: Unable to parse the query '(D{(host1 or host2) and host[1-5]}) or ((D{host[100-150]} and not D{host1[20-30]}) and D{host1[01,15,30]})' with the global grammar and no default backend is set: E Expected end of text, found 'or' (at char 36), (line:1, col:37) ../../../cumin/query.py:56: InvalidQueryError _______________________________________________________________ TestDirectQuery.test_execute ________________________________________________________________ self = <cumin.tests.unit.backends.test_direct.TestDirectQuery object at 0x7fda659037c0> def test_execute(self): """Calling execute() should return the list of hosts.""" assert self.query.execute('host1 or host2') == nodeset('host[1-2]') assert self.query.execute('host1 and host2') == nodeset() > assert self.query.execute('host1 and not host2') == nodeset('host1') ../../../cumin/tests/unit/backends/test_direct.py:29: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../cumin/backends/__init__.py:47: in execute self._build(query_string) ../../../cumin/backends/__init__.py:111: in _build super()._build(query_string) ../../../cumin/backends/__init__.py:76: in _build parsed = self.grammar.parseString(query_string.strip(), parseAll=True) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = Forward: {Group:({{~{{'and not' | 'and' | 'xor' | 'or'}} W:(!&,-.0-9A-[]-...)} | {{'(' : ...} ')'}}) [Group:({'and not' | 'and' | 'xor' | 'or'} {{~{{'and not' | 'and' | 'xor' | 'or'}} W:(!&,-.0-9A-[]-...)} | {{'(' : ...} ')'}})]...} instring = 'host1 and not host2', parse_all = False def parse_string( self, instring: str, parse_all: bool = False, *, parseAll: bool = False ) -> ParseResults: """ Parse a string with respect to the parser definition. This function is intended as the primary interface to the client code. :param instring: The input string to be parsed. :param parse_all: If set, the entire input string must match the grammar. :param parseAll: retained for pre-PEP8 compatibility, will be removed in a future release. :raises ParseException: Raised if ``parse_all`` is set and the input string does not match the whole grammar. :returns: the parsed data as a :class:`ParseResults` object, which may be accessed as a `list`, a `dict`, or an object with attributes if the given parser includes results names. If the input string is required to match the entire grammar, ``parse_all`` flag must be set to ``True``. This is also equivalent to ending the grammar with :class:`StringEnd`(). To report proper column numbers, ``parse_string`` operates on a copy of the input string where all tabs are converted to spaces (8 spaces per tab, as per the default in ``string.expandtabs``). If the input string contains tabs and the grammar uses parse actions that use the ``loc`` argument to index into the string being parsed, one can ensure a consistent view of the input string by doing one of the following: - calling ``parse_with_tabs`` on your grammar before calling ``parse_string`` (see :class:`parse_with_tabs`), - define your parse action using the full ``(s,loc,toks)`` signature, and reference the input string using the parse action's ``s`` argument, or - explicitly expand the tabs in your input string before calling ``parse_string``. Examples: By default, partial matches are OK. >>> res = Word('a').parse_string('aaaaabaaa') >>> print(res) ['aaaaa'] The parsing behavior varies by the inheriting class of this abstract class. Please refer to the children directly to see more examples. It raises an exception if parse_all flag is set and instring does not match the whole grammar. >>> res = Word('a').parse_string('aaaaabaaa', parse_all=True) Traceback (most recent call last): ... pyparsing.ParseException: Expected end of text, found 'b' (at char 5), (line:1, col:6) """ parseAll = parse_all or parseAll ParserElement.reset_cache() if not self.streamlined: self.streamline() for e in self.ignoreExprs: e.streamline() if not self.keepTabs: instring = instring.expandtabs() try: loc, tokens = self._parse(instring, 0) if parseAll: loc = self.preParse(instring, loc) se = Empty() + StringEnd() se._parse(instring, loc) except ParseBaseException as exc: if ParserElement.verbose_stacktrace: raise else: # catch and re-raise exception from here, clearing out pyparsing internal stack trace > raise exc.with_traceback(None) E pyparsing.exceptions.ParseException: Expected end of text, found 'host2' (at char 14), (line:1, col:15) /usr/lib/python3/dist-packages/pyparsing/core.py:1134: ParseException ________________________________________________________________ test_valid_grammars[direct] ________________________________________________________________ backend_name = 'direct' @pytest.mark.parametrize('backend_name', BACKENDS) def test_valid_grammars(backend_name): """Run quick pyparsing test over valid grammar strings for each backend that has the appropriate fixture.""" try: backend = importlib.import_module('cumin.backends.{backend}'.format(backend=backend_name)) except ImportError: return # Backend not available results = backend.grammar().runTests( get_fixture(os.path.join(BASE_PATH, '{backend}_valid.txt'.format(backend=backend_name)), as_string=True)) > assert results[0] E assert False ../../../cumin/tests/unit/backends/test_grammars.py:26: AssertionError ------------------------------------------------------------------- Captured stdout call -------------------------------------------------------------------- # Valid grammars hostname [['hostname']] [0]: ['hostname'] - hosts: ['hostname'] host-name [['host-name']] [0]: ['host-name'] - hosts: ['host-name'] host_name.domain [['host_name.domain']] [0]: ['host_name.domain'] - hosts: ['host_name.domain'] hostname and host_name.domain.tld [['hostname'], ['and', 'host_name.domain.tld']] [0]: ['hostname'] - hosts: ['hostname'] [1]: ['and', 'host_name.domain.tld'] - bool: 'and' - hosts: ['host_name.domain.tld'] host1 or host2 [['host1'], ['or', 'host2']] [0]: ['host1'] - hosts: ['host1'] [1]: ['or', 'host2'] - bool: 'or' - hosts: ['host2'] host1 and host2 [['host1'], ['and', 'host2']] [0]: ['host1'] - hosts: ['host1'] [1]: ['and', 'host2'] - bool: 'and' - hosts: ['host2'] host1 and not host2 host1 and not host2 ^ ParseException: Expected end of text, found 'host2' (at char 14), (line:1, col:15) FAIL: Expected end of text, found 'host2' (at char 14), (line:1, col:15) host10[10-20,30-50] [['host10[10-20,30-50]']] [0]: ['host10[10-20,30-50]'] - hosts: ['host10[10-20,30-50]'] (hostname.domain.tld) [['(', ['hostname.domain.tld'], ')']] [0]: ['(', ['hostname.domain.tld'], ')'] - close_subgroup: ')' - open_subgroup: '(' [0]: ( [1]: ['hostname.domain.tld'] - hosts: ['hostname.domain.tld'] [2]: ) (host1 or host2) and host1 [['(', ['host1'], ['or', 'host2'], ')'], ['and', 'host1']] [0]: ['(', ['host1'], ['or', 'host2'], ')'] - close_subgroup: ')' - open_subgroup: '(' [0]: ( [1]: ['host1'] - hosts: ['host1'] [2]: ['or', 'host2'] - bool: 'or' - hosts: ['host2'] [3]: ) [1]: ['and', 'host1'] - bool: 'and' - hosts: ['host1'] ((host1[0-9] or host01) and host[01-10]) [['(', ['(', ['host1[0-9]'], ['or', 'host01'], ')'], ['and', 'host[01-10]'], ')']] [0]: ['(', ['(', ['host1[0-9]'], ['or', 'host01'], ')'], ['and', 'host[01-10]'], ')'] - close_subgroup: ')' - open_subgroup: '(' [0]: ( [1]: ['(', ['host1[0-9]'], ['or', 'host01'], ')'] - close_subgroup: ')' - open_subgroup: '(' [0]: ( [1]: ['host1[0-9]'] - hosts: ['host1[0-9]'] [2]: ['or', 'host01'] - bool: 'or' - hosts: ['host01'] [3]: ) [2]: ['and', 'host[01-10]'] - bool: 'and' - hosts: ['host[01-10]'] [3]: ) ______________________________________________________________ test_valid_grammars[knownhosts] ______________________________________________________________ backend_name = 'knownhosts' @pytest.mark.parametrize('backend_name', BACKENDS) def test_valid_grammars(backend_name): """Run quick pyparsing test over valid grammar strings for each backend that has the appropriate fixture.""" try: backend = importlib.import_module('cumin.backends.{backend}'.format(backend=backend_name)) except ImportError: return # Backend not available results = backend.grammar().runTests( get_fixture(os.path.join(BASE_PATH, '{backend}_valid.txt'.format(backend=backend_name)), as_string=True)) > assert results[0] E assert False ../../../cumin/tests/unit/backends/test_grammars.py:26: AssertionError ------------------------------------------------------------------- Captured stdout call -------------------------------------------------------------------- # Valid grammars hostname [['hostname']] [0]: ['hostname'] - hosts: ['hostname'] host-name [['host-name']] [0]: ['host-name'] - hosts: ['host-name'] host_name.domain [['host_name.domain']] [0]: ['host_name.domain'] - hosts: ['host_name.domain'] hostname and host_name.domain.tld [['hostname'], ['and', 'host_name.domain.tld']] [0]: ['hostname'] - hosts: ['hostname'] [1]: ['and', 'host_name.domain.tld'] - bool: 'and' - hosts: ['host_name.domain.tld'] host1 or host2 [['host1'], ['or', 'host2']] [0]: ['host1'] - hosts: ['host1'] [1]: ['or', 'host2'] - bool: 'or' - hosts: ['host2'] host1 and host2 [['host1'], ['and', 'host2']] [0]: ['host1'] - hosts: ['host1'] [1]: ['and', 'host2'] - bool: 'and' - hosts: ['host2'] host1 and not host2 host1 and not host2 ^ ParseException: Expected end of text, found 'host2' (at char 14), (line:1, col:15) FAIL: Expected end of text, found 'host2' (at char 14), (line:1, col:15) host10[10-20,30-50] [['host10[10-20,30-50]']] [0]: ['host10[10-20,30-50]'] - hosts: ['host10[10-20,30-50]'] host10[10-20,30-50]* [['host10[10-20,30-50]*']] [0]: ['host10[10-20,30-50]*'] - hosts: ['host10[10-20,30-50]*'] host?0[10-20,30-50]* [['host?0[10-20,30-50]*']] [0]: ['host?0[10-20,30-50]*'] - hosts: ['host?0[10-20,30-50]*'] (hostname.domain.tld) [['(', ['hostname.domain.tld'], ')']] [0]: ['(', ['hostname.domain.tld'], ')'] - close_subgroup: ')' - open_subgroup: '(' [0]: ( [1]: ['hostname.domain.tld'] - hosts: ['hostname.domain.tld'] [2]: ) (host1 or host2) and host1 [['(', ['host1'], ['or', 'host2'], ')'], ['and', 'host1']] [0]: ['(', ['host1'], ['or', 'host2'], ')'] - close_subgroup: ')' - open_subgroup: '(' [0]: ( [1]: ['host1'] - hosts: ['host1'] [2]: ['or', 'host2'] - bool: 'or' - hosts: ['host2'] [3]: ) [1]: ['and', 'host1'] - bool: 'and' - hosts: ['host1'] ((host1[0-9] or host01) and host[01-10]) [['(', ['(', ['host1[0-9]'], ['or', 'host01'], ')'], ['and', 'host[01-10]'], ')']] [0]: ['(', ['(', ['host1[0-9]'], ['or', 'host01'], ')'], ['and', 'host[01-10]'], ')'] - close_subgroup: ')' - open_subgroup: '(' [0]: ( [1]: ['(', ['host1[0-9]'], ['or', 'host01'], ')'] - close_subgroup: ')' - open_subgroup: '(' [0]: ( [1]: ['host1[0-9]'] - hosts: ['host1[0-9]'] [2]: ['or', 'host01'] - bool: 'or' - hosts: ['host01'] [3]: ) [2]: ['and', 'host[01-10]'] - bool: 'and' - hosts: ['host[01-10]'] [3]: ) _________________________________________________________ TestKnownhostsQuery.test_execute_and_not __________________________________________________________ self = <cumin.tests.unit.backends.test_knownhosts.TestKnownhostsQuery object at 0x7fda656ef130> def test_execute_and_not(self): """Calling execute() with two hosts with 'and not' should return the first host.""" expected = NodeSet('host1.domain', resolver=RESOLVER_NOGROUP) > assert self.query.execute('host1.domain and not host2.domain') == > expected ../../../cumin/tests/unit/backends/test_knownhosts.py:60: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../cumin/backends/__init__.py:47: in execute self._build(query_string) ../../../cumin/backends/knownhosts.py:103: in _build super()._build(query_string) ../../../cumin/backends/__init__.py:111: in _build super()._build(query_string) ../../../cumin/backends/__init__.py:76: in _build parsed = self.grammar.parseString(query_string.strip(), parseAll=True) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = Forward: {Group:({{~{{'and not' | 'and' | 'xor' | 'or'}} W:(!&*,-.0-9?A-[...)} | {{'(' : ...} ')'}}) [Group:({'and not' | 'and' | 'xor' | 'or'} {{~{{'and not' | 'and' | 'xor' | 'or'}} W:(!&*,-.0-9?A-[...)} | {{'(' : ...} ')'}})]...} instring = 'host1.domain and not host2.domain', parse_all = False def parse_string( self, instring: str, parse_all: bool = False, *, parseAll: bool = False ) -> ParseResults: """ Parse a string with respect to the parser definition. This function is intended as the primary interface to the client code. :param instring: The input string to be parsed. :param parse_all: If set, the entire input string must match the grammar. :param parseAll: retained for pre-PEP8 compatibility, will be removed in a future release. :raises ParseException: Raised if ``parse_all`` is set and the input string does not match the whole grammar. :returns: the parsed data as a :class:`ParseResults` object, which may be accessed as a `list`, a `dict`, or an object with attributes if the given parser includes results names. If the input string is required to match the entire grammar, ``parse_all`` flag must be set to ``True``. This is also equivalent to ending the grammar with :class:`StringEnd`(). To report proper column numbers, ``parse_string`` operates on a copy of the input string where all tabs are converted to spaces (8 spaces per tab, as per the default in ``string.expandtabs``). If the input string contains tabs and the grammar uses parse actions that use the ``loc`` argument to index into the string being parsed, one can ensure a consistent view of the input string by doing one of the following: - calling ``parse_with_tabs`` on your grammar before calling ``parse_string`` (see :class:`parse_with_tabs`), - define your parse action using the full ``(s,loc,toks)`` signature, and reference the input string using the parse action's ``s`` argument, or - explicitly expand the tabs in your input string before calling ``parse_string``. Examples: By default, partial matches are OK. >>> res = Word('a').parse_string('aaaaabaaa') >>> print(res) ['aaaaa'] The parsing behavior varies by the inheriting class of this abstract class. Please refer to the children directly to see more examples. It raises an exception if parse_all flag is set and instring does not match the whole grammar. >>> res = Word('a').parse_string('aaaaabaaa', parse_all=True) Traceback (most recent call last): ... pyparsing.ParseException: Expected end of text, found 'b' (at char 5), (line:1, col:6) """ parseAll = parse_all or parseAll ParserElement.reset_cache() if not self.streamlined: self.streamline() for e in self.ignoreExprs: e.streamline() if not self.keepTabs: instring = instring.expandtabs() try: loc, tokens = self._parse(instring, 0) if parseAll: loc = self.preParse(instring, loc) se = Empty() + StringEnd() se._parse(instring, loc) except ParseBaseException as exc: if ParserElement.verbose_stacktrace: raise else: # catch and re-raise exception from here, clearing out pyparsing internal stack trace > raise exc.with_traceback(None) E pyparsing.exceptions.ParseException: Expected end of text, found 'host2' (at char 21), (line:1, col:22) /usr/lib/python3/dist-packages/pyparsing/core.py:1134: ParseException --------------------------------------------------------------------- Captured log call --------------------------------------------------------------------- WARNING cumin.backends.knownhosts.KnownHostsQuery:knownhosts.py:161 Discarded invalid line 24 (not enough fields) in known hosts file '/home/anarcat/dist/cumin/cumin/tests/fixtures/backends/knownhosts.txt': host10.domain ssh-rsa WARNING cumin.backends.knownhosts.KnownHostsQuery:knownhosts.py:161 Discarded invalid line 26 (not enough fields) in known hosts file '/home/anarcat/dist/cumin/cumin/tests/fixtures/backends/knownhosts.txt': @cert-authority host11.domain ssh-rsa WARNING cumin.backends.knownhosts.KnownHostsQuery:knownhosts.py:161 Discarded invalid line 28 (unknown marker) in known hosts file '/home/anarcat/dist/cumin/cumin/tests/fixtures/backends/knownhosts.txt': @marker host12.domain ssh-rsa AAAA...= WARNING cumin.backends.knownhosts.KnownHostsQuery:knownhosts.py:161 Discarded invalid line 50 (not enough fields) in known hosts file '/home/anarcat/dist/cumin/cumin/tests/fixtures/backends/knownhosts.txt': invalid line _________________________________________________________ TestKnownhostsQuery.test_execute_complex __________________________________________________________ self = <cumin.tests.unit.backends.test_knownhosts.TestKnownhostsQuery object at 0x7fda656eee60> def test_execute_complex(self): """Calling execute() with a complex query should return the matching hosts.""" expected = NodeSet('host[1,5,8].domain', resolver=RESOLVER_NOGROUP) > assert self.query.execute('host1.domain or (host[5-9].domain and not > host7.domain)') == expected ../../../cumin/tests/unit/backends/test_knownhosts.py:70: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ ../../../cumin/backends/__init__.py:47: in execute self._build(query_string) ../../../cumin/backends/knownhosts.py:103: in _build super()._build(query_string) ../../../cumin/backends/__init__.py:111: in _build super()._build(query_string) ../../../cumin/backends/__init__.py:76: in _build parsed = self.grammar.parseString(query_string.strip(), parseAll=True) _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = Forward: {Group:({{~{{'and not' | 'and' | 'xor' | 'or'}} W:(!&*,-.0-9?A-[...)} | {{'(' : ...} ')'}}) [Group:({'and not' | 'and' | 'xor' | 'or'} {{~{{'and not' | 'and' | 'xor' | 'or'}} W:(!&*,-.0-9?A-[...)} | {{'(' : ...} ')'}})]...} instring = 'host1.domain or (host[5-9].domain and not host7.domain)', parse_all = False def parse_string( self, instring: str, parse_all: bool = False, *, parseAll: bool = False ) -> ParseResults: """ Parse a string with respect to the parser definition. This function is intended as the primary interface to the client code. :param instring: The input string to be parsed. :param parse_all: If set, the entire input string must match the grammar. :param parseAll: retained for pre-PEP8 compatibility, will be removed in a future release. :raises ParseException: Raised if ``parse_all`` is set and the input string does not match the whole grammar. :returns: the parsed data as a :class:`ParseResults` object, which may be accessed as a `list`, a `dict`, or an object with attributes if the given parser includes results names. If the input string is required to match the entire grammar, ``parse_all`` flag must be set to ``True``. This is also equivalent to ending the grammar with :class:`StringEnd`(). To report proper column numbers, ``parse_string`` operates on a copy of the input string where all tabs are converted to spaces (8 spaces per tab, as per the default in ``string.expandtabs``). If the input string contains tabs and the grammar uses parse actions that use the ``loc`` argument to index into the string being parsed, one can ensure a consistent view of the input string by doing one of the following: - calling ``parse_with_tabs`` on your grammar before calling ``parse_string`` (see :class:`parse_with_tabs`), - define your parse action using the full ``(s,loc,toks)`` signature, and reference the input string using the parse action's ``s`` argument, or - explicitly expand the tabs in your input string before calling ``parse_string``. Examples: By default, partial matches are OK. >>> res = Word('a').parse_string('aaaaabaaa') >>> print(res) ['aaaaa'] The parsing behavior varies by the inheriting class of this abstract class. Please refer to the children directly to see more examples. It raises an exception if parse_all flag is set and instring does not match the whole grammar. >>> res = Word('a').parse_string('aaaaabaaa', parse_all=True) Traceback (most recent call last): ... pyparsing.ParseException: Expected end of text, found 'b' (at char 5), (line:1, col:6) """ parseAll = parse_all or parseAll ParserElement.reset_cache() if not self.streamlined: self.streamline() for e in self.ignoreExprs: e.streamline() if not self.keepTabs: instring = instring.expandtabs() try: loc, tokens = self._parse(instring, 0) if parseAll: loc = self.preParse(instring, loc) se = Empty() + StringEnd() se._parse(instring, loc) except ParseBaseException as exc: if ParserElement.verbose_stacktrace: raise else: # catch and re-raise exception from here, clearing out pyparsing internal stack trace > raise exc.with_traceback(None) E pyparsing.exceptions.ParseException: Expected end of text, found 'or' (at char 13), (line:1, col:14) /usr/lib/python3/dist-packages/pyparsing/core.py:1134: ParseException --------------------------------------------------------------------- Captured log call --------------------------------------------------------------------- WARNING cumin.backends.knownhosts.KnownHostsQuery:knownhosts.py:161 Discarded invalid line 24 (not enough fields) in known hosts file '/home/anarcat/dist/cumin/cumin/tests/fixtures/backends/knownhosts.txt': host10.domain ssh-rsa WARNING cumin.backends.knownhosts.KnownHostsQuery:knownhosts.py:161 Discarded invalid line 26 (not enough fields) in known hosts file '/home/anarcat/dist/cumin/cumin/tests/fixtures/backends/knownhosts.txt': @cert-authority host11.domain ssh-rsa WARNING cumin.backends.knownhosts.KnownHostsQuery:knownhosts.py:161 Discarded invalid line 28 (unknown marker) in known hosts file '/home/anarcat/dist/cumin/cumin/tests/fixtures/backends/knownhosts.txt': @marker host12.domain ssh-rsa AAAA...= WARNING cumin.backends.knownhosts.KnownHostsQuery:knownhosts.py:161 Discarded invalid line 50 (not enough fields) in known hosts file '/home/anarcat/dist/cumin/cumin/tests/fixtures/backends/knownhosts.txt': invalid line ================================================================== short test summary info ================================================================== FAILED ../../../cumin/tests/unit/test_grammar.py::test_valid_strings - assert False FAILED ../../../cumin/tests/unit/test_query.py::test_execute_global_and_not - cumin.backends.InvalidQueryError: Unable to parse the query 'D{host[1-5]} an... FAILED ../../../cumin/tests/unit/test_query.py::test_execute_subgroups - cumin.backends.InvalidQueryError: Unable to parse the query '(D{host1} or D{host2... FAILED ../../../cumin/tests/unit/test_query.py::test_execute_complex_global - cumin.backends.InvalidQueryError: Unable to parse the query '(D{(host1 or ho... FAILED ../../../cumin/tests/unit/backends/test_direct.py::TestDirectQuery::test_execute - pyparsing.exceptions.ParseException: Expected end of text, found... FAILED ../../../cumin/tests/unit/backends/test_grammars.py::test_valid_grammars[direct] - assert False FAILED ../../../cumin/tests/unit/backends/test_grammars.py::test_valid_grammars[knownhosts] - assert False FAILED ../../../cumin/tests/unit/backends/test_knownhosts.py::TestKnownhostsQuery::test_execute_and_not - pyparsing.exceptions.ParseException: Expected en... FAILED ../../../cumin/tests/unit/backends/test_knownhosts.py::TestKnownhostsQuery::test_execute_complex - pyparsing.exceptions.ParseException: Expected en... =============================================================== 9 failed, 407 passed in 1.55s =============================================================== E: pybuild pybuild:386: test: plugin distutils failed with: exit code=1: cd /home/anarcat/dist/cumin/.pybuild/cpython3_3.10_cumin/build; python3.10 -m pytest {dir}/cumin/tests/unit dh_auto_test: error: pybuild --test -i python{version} -p 3.10 returned exit code 13 make: *** [debian/rules:13 : build] Erreur 13 dpkg-buildpackage: error: debian/rules build subprocess returned exit status 2