File: parsetricks.py

package info (click to toggle)
gavodachs 2.3%2Bdfsg-3
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 7,260 kB
  • sloc: python: 58,359; xml: 8,882; javascript: 3,453; ansic: 661; sh: 158; makefile: 22
file content (106 lines) | stat: -rw-r--r-- 3,408 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
"""
A wrapper around pyparsing.  We need this because of the various
idiosyncracies pyparsing has had over the years, and also because pyparsing
is not terribly well suited for the multi-grammar situation we have here.

Hence, whenever you use pyparsing in DaCHS: Use parsetricks instead of
pyparsing.
"""

#c Copyright 2008-2021, the GAVO project <gavo@ari.uni-heidelberg.de>
#c
#c This program is free software, covered by the GNU GPL.  See the
#c COPYING file in the source distribution.


# Not checked by pyflakes: (effectively) API file with gratuitous imports

import contextlib
import threading

from gavo.imp.pyparsing import *


####################### Pyparsing hacks
#
# (1) When building grammars, always do so using the pyparsingWhitechars
# context manager.  Building grammars is thread-safe, but different
# grammars here use different whitespace conventions, so without
# the c.m., you might get those messed up.
#
# (2) When parsing strings, *always* go through pyparseString(grammar,
# string) and fellow functions whenever your code could run from within
# the server (i.e., basically always outside of tests).
# pyparsing is not thread-safe, and thus we'll need to shoehorn some
# locking on top of it; I don't want to change the pyparsing methods
# themselves since they may be called very frequently.
#
# 
# Ugly hack alert: We currently (2021) have to pull a 2.2 version of
# pyparsing from imp, because the upstream 2.4 has changes that require,
# in effect, rewrites of the ADQL and STC-S handling, which we don't
# want to rush.


ParserElement.enablePackrat()

_PYPARSE_LOCK = threading.RLock()

@contextlib.contextmanager
def pyparsingWhitechars(whiteChars):
	"""a context manager that serializes pyparsing grammar compilation
	and manages its whitespace chars.

	We need different whitespace definitions in some parts of DaCHS.
	(The default used to be " \\t" for a while, so this is what things
	get reset to).

	Since whitespace apparently can only be set globally for pyparsing,
	we provide this c.m.  Since it is possible that grammars will be
	compiled in threads (e.g., as a side effect of getRD), this is
	protected by a lock.  This, in turn, means that this can 
	potentially block for a fairly long time.

	Bottom line: When compiling pyparsing grammars, *always* set
	the whitespace chars explicitely, and do it through this c.m.
	"""
	_PYPARSE_LOCK.acquire()
	ParserElement.setDefaultWhitespaceChars(whiteChars)
	try:
		yield
	finally:
		ParserElement.setDefaultWhitespaceChars(" \t")
		_PYPARSE_LOCK.release()


def pyparseString(grammar, string, **kwargs):
	"""parses a string using a pyparsing grammar thread-safely.
	"""
	with _PYPARSE_LOCK:
		res = grammar.parseString(string, **kwargs)
		ParserElement.resetCache()
		return res


def pyparseTransform(grammar, string, **kwargs):
	"""calls grammar's transformString method thread-safely.
	"""
	with _PYPARSE_LOCK:
		return grammar.transformString(string, **kwargs)


# we monkeypatch pyparsing (since 1.5.2) to not deep copy
# expressions; the way newer pyparsings have been doing this
# is incompatible with the way we bind actions to parser elements
# in ADQL (perhaps we should change this?).
def _parse_expression_copy(self):
	ret = super(ParseExpression,self).copy()
	ret.exprs = self.exprs[:]  # CHANGE wrt upstream
	return ret


ParseExpression.copy = _parse_expression_copy

del _parse_expression_copy