Commit ca4f1b14 authored by Nsukami Patrick's avatar Nsukami Patrick

black: Format without any compromise

- update tox configuration, add black environment
- format the whole project
- update Gitlab CI config add a job for black
parent fe99689fd731
......@@ -42,6 +42,13 @@ flake8:
stage: tests
script: tox -e flake8
black:
stage: tests
rules:
- changes:
- "**/*.py"
script: tox -e black
mypy:
except:
variables:
......
......@@ -27,9 +27,9 @@ __docformat__ = "restructuredtext en"
modname = "rql"
numversion = (0, 37, 0)
version = '.'.join(str(num) for num in numversion)
version = ".".join(str(num) for num in numversion)
license = 'LGPL'
license = "LGPL"
author = "Logilab"
author_email = "contact@logilab.fr"
......@@ -42,12 +42,12 @@ web = "https://forge.extranet.logilab.fr/CubicWeb/RQL/"
ftp = "ftp://ftp.logilab.org/pub/rql"
classifiers = [
'License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3 :: Only',
'Topic :: Database',
'Topic :: Software Development :: Libraries :: Python Modules',
"License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"Topic :: Database",
"Topic :: Software Development :: Libraries :: Python Modules",
]
......@@ -56,47 +56,58 @@ include_dirs = []
def gecode_version():
version = [3, 3, 1]
if osp.exists('data/gecode_version.cc'):
if osp.exists("data/gecode_version.cc"):
try:
subprocess.check_call(['g++', '-o', 'gecode_version', 'data/gecode_version.cc'])
subprocess.check_call(
["g++", "-o", "gecode_version", "data/gecode_version.cc"]
)
p = subprocess.Popen("./gecode_version", stdout=subprocess.PIPE)
vers = p.communicate()[0].decode('ascii')
version = [int(c) for c in vers.strip().split('.')]
vers = p.communicate()[0].decode("ascii")
version = [int(c) for c in vers.strip().split(".")]
except (EnvironmentError, subprocess.CalledProcessError):
pass
return version
def encode_version(a, b, c):
return ((a << 16)+(b << 8)+c)
return (a << 16) + (b << 8) + c
GECODE_VERSION = encode_version(*gecode_version())
if sys.platform != 'win32':
ext_modules = [Extension('rql.rql_solve',
['rql/gecode_solver.cpp'],
libraries=['gecodeint', 'gecodekernel', 'gecodesearch', ],
extra_compile_args=['-DGE_VERSION=%s' % GECODE_VERSION],
)
]
if sys.platform != "win32":
ext_modules = [
Extension(
"rql.rql_solve",
["rql/gecode_solver.cpp"],
libraries=[
"gecodeint",
"gecodekernel",
"gecodesearch",
],
extra_compile_args=["-DGE_VERSION=%s" % GECODE_VERSION],
)
]
else:
ext_modules = [Extension('rql.rql_solve',
['rql/gecode_solver.cpp'],
libraries=['GecodeInt-3-3-1-r-x86',
'GecodeKernel-3-3-1-r-x86',
'GecodeSearch-3-3-1-r-x86',
'GecodeSupport-3-3-1-r-x86',
],
extra_compile_args=['/DGE_VERSION=%s' % GECODE_VERSION, '/EHsc'],
# extra_link_args=['-static-libgcc'],
)
]
ext_modules = [
Extension(
"rql.rql_solve",
["rql/gecode_solver.cpp"],
libraries=[
"GecodeInt-3-3-1-r-x86",
"GecodeKernel-3-3-1-r-x86",
"GecodeSearch-3-3-1-r-x86",
"GecodeSupport-3-3-1-r-x86",
],
extra_compile_args=["/DGE_VERSION=%s" % GECODE_VERSION, "/EHsc"],
# extra_link_args=['-static-libgcc'],
)
]
install_requires = [
'logilab-common >= 1.6.0',
'logilab-database >= 1.6.0',
'yapps2 >= 2.2.0', # XXX to ensure we don't use the broken pypi version
'logilab-constraint >= 0.5.0', # fallback if the gecode compiled module is missing
'setuptools',
]
"logilab-common >= 1.6.0",
"logilab-database >= 1.6.0",
"yapps2 >= 2.2.0", # XXX to ensure we don't use the broken pypi version
"logilab-constraint >= 0.5.0", # fallback if the gecode compiled module is missing
"setuptools",
]
......@@ -19,14 +19,14 @@
# -- Project information -----------------------------------------------------
project = 'RQL'
copyright = '2021, Logilab'
author = 'Logilab'
project = "RQL"
copyright = "2021, Logilab"
author = "Logilab"
# The short X.Y version
version = ''
version = ""
# The full version, including alpha/beta/rc tags
release = ''
release = ""
# -- General configuration ---------------------------------------------------
......@@ -39,23 +39,23 @@ release = ''
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.doctest',
'sphinx.ext.intersphinx',
'sphinx.ext.viewcode',
"sphinx.ext.autodoc",
"sphinx.ext.doctest",
"sphinx.ext.intersphinx",
"sphinx.ext.viewcode",
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
templates_path = ["_templates"]
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
source_suffix = ".rst"
# The master toctree document.
master_doc = 'index'
master_doc = "index"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
......@@ -67,7 +67,7 @@ language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = None
......@@ -78,7 +78,7 @@ pygments_style = None
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
html_theme = "alabaster"
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
......@@ -89,7 +89,7 @@ html_theme = 'alabaster'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
html_static_path = ["_static"]
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
......@@ -105,7 +105,7 @@ html_static_path = ['_static']
# -- Options for HTMLHelp output ---------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'RQLdoc'
htmlhelp_basename = "RQLdoc"
# -- Options for LaTeX output ------------------------------------------------
......@@ -114,15 +114,12 @@ latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
......@@ -132,8 +129,7 @@ latex_elements = {
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'RQL.tex', 'RQL Documentation',
'Logilab', 'manual'),
(master_doc, "RQL.tex", "RQL Documentation", "Logilab", "manual"),
]
......@@ -141,10 +137,7 @@ latex_documents = [
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'rql', 'RQL Documentation',
[author], 1)
]
man_pages = [(master_doc, "rql", "RQL Documentation", [author], 1)]
# -- Options for Texinfo output ----------------------------------------------
......@@ -153,9 +146,15 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'RQL', 'RQL Documentation',
author, 'RQL', 'One line description of project.',
'Miscellaneous'),
(
master_doc,
"RQL",
"RQL Documentation",
author,
"RQL",
"One line description of project.",
"Miscellaneous",
),
]
......@@ -174,7 +173,7 @@ epub_title = project
# epub_uid = ''
# A list of files that should not be packed into the epub file.
epub_exclude_files = ['search.html']
epub_exclude_files = ["search.html"]
# -- Extension configuration -------------------------------------------------
......@@ -182,4 +181,4 @@ epub_exclude_files = ['search.html']
# -- Options for intersphinx extension ---------------------------------------
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'https://docs.python.org/': None}
intersphinx_mapping = {"https://docs.python.org/": None}
......@@ -39,7 +39,7 @@ from rql._exceptions import ( # noqa
)
__version__ = pkg_resources.get_distribution('rql').version
__version__ = pkg_resources.get_distribution("rql").version
# REQUIRED_TYPES = ['String', 'Float', 'Int', 'Boolean', 'Date']
......@@ -52,36 +52,48 @@ class RQLHelper:
- comparison of two queries
"""
def __init__(self, schema, uid_func_mapping=None, special_relations=None,
resolver_class=None, backend=None):
def __init__(
self,
schema,
uid_func_mapping=None,
special_relations=None,
resolver_class=None,
backend=None,
):
# chech schema
# for e_type in REQUIRED_TYPES:
# if not schema.has_entity(e_type):
# raise MissingType(e_type)
# create helpers
from rql.stcheck import RQLSTChecker, RQLSTAnnotator
special_relations = special_relations or {}
if uid_func_mapping:
for key in uid_func_mapping:
special_relations[key] = 'uid'
special_relations[key] = "uid"
self._checker = RQLSTChecker(schema, special_relations, backend)
self._annotator = RQLSTAnnotator(schema, special_relations)
self._analyser_lock = threading.Lock()
if resolver_class is None:
from rql.analyze import ETypeResolver
resolver_class = ETypeResolver
self._analyser = resolver_class(schema, uid_func_mapping)
# IgnoreTypeRestriction analyser
from rql.analyze import ETypeResolverIgnoreTypeRestriction
self._itr_analyser_lock = threading.Lock()
self._itr_analyser = ETypeResolverIgnoreTypeRestriction(schema, uid_func_mapping)
self._itr_analyser = ETypeResolverIgnoreTypeRestriction(
schema, uid_func_mapping
)
self.set_schema(schema)
def set_schema(self, schema):
from rql.utils import is_keyword
for etype in schema.entities():
etype = str(etype)
if is_keyword(etype) or etype.capitalize() == 'Any':
if is_keyword(etype) or etype.capitalize() == "Any":
raise UsesReservedWord(etype)
for rtype in schema.relations():
rtype = str(rtype)
......@@ -96,6 +108,7 @@ class RQLHelper:
def set_backend(self, backend):
self._checker.backend = backend
backend = property(get_backend, set_backend)
def parse(self, rqlstring, annotate=True):
......@@ -110,25 +123,23 @@ class RQLHelper:
def annotate(self, rqlst):
self._annotator.annotate(rqlst)
def compute_solutions(self, rqlst, uid_func_mapping=None, kwargs=None,
debug=False):
def compute_solutions(self, rqlst, uid_func_mapping=None, kwargs=None, debug=False):
"""Set solutions for variables of the syntax tree.
Each solution is a dictionary with variable's name as key and
variable's type as value.
"""
with self._analyser_lock:
return self._analyser.visit(rqlst, uid_func_mapping, kwargs,
debug)
return self._analyser.visit(rqlst, uid_func_mapping, kwargs, debug)
def compute_all_solutions(self, rqlst, uid_func_mapping=None, kwargs=None,
debug=False):
def compute_all_solutions(
self, rqlst, uid_func_mapping=None, kwargs=None, debug=False
):
"""compute syntax tree solutions with all types restriction (eg
is/instance_of relations) ignored
"""
with self._itr_analyser_lock:
self._itr_analyser.visit(rqlst, uid_func_mapping, kwargs,
debug)
self._itr_analyser.visit(rqlst, uid_func_mapping, kwargs, debug)
def simplify(self, rqlst):
"""Simplify `rqlst` by rewriting non-final variables associated to a const
......@@ -137,7 +148,7 @@ class RQLHelper:
The tree is modified in-place.
"""
# print('simplify', rqlst.as_string())
if rqlst.TYPE == 'select':
if rqlst.TYPE == "select":
for select in rqlst.children:
self._simplify(select)
......@@ -149,8 +160,8 @@ class RQLHelper:
rewritten = False
for var in list(select.defined_vars.values()):
stinfo = var.stinfo
if stinfo['constnode'] and not stinfo.get('blocsimplification'):
uidrel = stinfo['uidrel']
if stinfo["constnode"] and not stinfo.get("blocsimplification"):
uidrel = stinfo["uidrel"]
var = uidrel.children[0].variable
vconsts = []
rhs = uidrel.children[1].children[0]
......@@ -185,16 +196,16 @@ class RQLHelper:
elif rel is uidrel:
uidrel.parent.remove(uidrel)
elif rel.is_types_restriction():
stinfo['typerel'] = None
stinfo["typerel"] = None
rel.parent.remove(rel)
else:
rhs = copy_uid_node(select, rhs, vconsts)
vref.parent.replace(vref, rhs)
del select.defined_vars[var.name]
stinfo['uidrel'] = None
stinfo["uidrel"] = None
rewritten = True
if vconsts:
select.stinfo['rewritten'][var.name] = vconsts
select.stinfo["rewritten"][var.name] = vconsts
if rewritten and select.solutions:
select.clean_solutions()
......@@ -204,6 +215,7 @@ class RQLHelper:
Return True if both requests would return the same results.
"""
from rql.compare import compare_tree
return compare_tree(self.parse(rqlstring1), self.parse(rqlstring2))
......@@ -218,10 +230,11 @@ def parse(rqlstring, print_errors=True):
"""Return a syntax tree created from a RQL string."""
from yapps.runtime import print_error, SyntaxError, NoMoreTokens
from rql.parser import Hercule, HerculeScanner
# make sure rql string ends with a semi-colon
rqlstring = rqlstring.strip()
if rqlstring and not rqlstring.endswith(';'):
rqlstring += ';'
if rqlstring and not rqlstring.endswith(";"):
rqlstring += ";"
# parse the RQL string
parser = Hercule(HerculeScanner(rqlstring))
try:
......@@ -232,9 +245,9 @@ def parse(rqlstring, print_errors=True):
multi_lines_rql = rqlstring.splitlines()
nb_lines = len(multi_lines_rql)
if nb_lines > 5:
width = log(nb_lines, 10)+1
width = log(nb_lines, 10) + 1
template = " %%%ii: %%s" % width
rqlstring = '\n'.join(
rqlstring = "\n".join(
template % (idx + 1, line)
for idx, line in enumerate(multi_lines_rql)
)
......@@ -248,24 +261,30 @@ def parse(rqlstring, print_errors=True):
around = rql_line
here_left_padding = len("around: ") + column
elif column < padding:
around = rql_line[:padding * 2] + "..."
around = rql_line[: padding * 2] + "..."
here_left_padding = len("around: ") + column
elif (len(rql_line) - padding) < column:
around = "..." + rql_line[-(padding * 2):]
here_left_padding = (
len("around: ...") +
(column - (len(rql_line) - (padding * 2)))
around = "..." + rql_line[-(padding * 2) :]
here_left_padding = len("around: ...") + (
column - (len(rql_line) - (padding * 2))
)
else:
around = "..." + rql_line[column - padding:column + padding] + "..."
around = (
"..." + rql_line[column - padding : column + padding] + "..."
)
here_left_padding = len("around: ...") + padding
here = ' ' * (here_left_padding - 1) + '^ here'
msg = '%s in:\n%s\n\nat line %s and column %s\naround: %s\n%s' % (
ex.msg, rqlstring, line, column, around, here
here = " " * (here_left_padding - 1) + "^ here"
msg = "%s in:\n%s\n\nat line %s and column %s\naround: %s\n%s" % (
ex.msg,
rqlstring,
line,
column,
around,
here,
)
else:
msg = '%s\n%s' % (rqlstring, ex.msg)
msg = "%s\n%s" % (rqlstring, ex.msg)
exc = RQLSyntaxError(msg)
exc.__traceback__ = sys.exc_info()[-1]
raise exc
......@@ -282,12 +301,13 @@ def parse(rqlstring, print_errors=True):
raise exc
except ImportError: # duh?
sys.stderr = out
exc = RQLSyntaxError('Syntax Error', ex.msg, 'on line',
1 + out.count('\n', 0, ex.pos))
exc = RQLSyntaxError(
"Syntax Error", ex.msg, "on line", 1 + out.count("\n", 0, ex.pos)
)
exc.__traceback__ = sys.exc_info()[-1]
raise exc
except NoMoreTokens:
msg = 'Could not complete parsing; stopped around here: \n%s'
msg = "Could not complete parsing; stopped around here: \n%s"
exc = RQLSyntaxError(msg % parser._scanner)
exc.__traceback__ = sys.exc_info()[-1]
raise exc
......
......@@ -29,14 +29,15 @@ from rql import TypeResolverException, nodes
try:
pure = bool(os.environ.get('RQL_USE_PURE_PYTHON_ANALYSE', 0))
pure = bool(os.environ.get("RQL_USE_PURE_PYTHON_ANALYSE", 0))
if pure:
raise ImportError
from rql import rql_solve # type: ignore[attr-defined] # rql_solve is a cpp extension
except ImportError:
rql_solve = None
import warnings
warnings.filterwarnings(action='ignore', module='logilab.constraint.propagation')
warnings.filterwarnings(action="ignore", module="logilab.constraint.propagation")
from logilab.constraint import Repository, Solver, fd
# Gecode solver not available
......@@ -59,8 +60,8 @@ class ConstraintCSPProblem:
return self.output.getvalue()
def printer(self, *msgs):
self.output.write(' '.join(str(msg) for msg in msgs))
self.output.write('\n')
self.output.write(" ".join(str(msg) for msg in msgs))
self.output.write("\n")
def solve(self):
repo = Repository(self.domains.keys(), self.domains, self.get_constraints())
......@@ -90,7 +91,7 @@ class ConstraintCSPProblem:
def var_has_type(self, var, etype):
assert isinstance(etype, str)
self.add_expr((var,), '%s == %r' % (var, etype))
self.add_expr((var,), "%s == %r" % (var, etype))
def var_has_types(self, var, etypes):
etypes = tuple(etypes)
......@@ -99,11 +100,11 @@ class ConstraintCSPProblem:
if len(etypes) == 1:
cstr = '%s == "%s"' % (var, etypes[0])
else:
cstr = '%s in %s ' % (var, etypes)
cstr = "%s in %s " % (var, etypes)
self.add_expr((var,), cstr)
def vars_have_same_types(self, varnames, types):
self.add_expr(varnames, '%s in %s' % ('=='.join(varnames), types))
self.add_expr(varnames, "%s in %s" % ("==".join(varnames), types))
def or_and(self, equalities):
orred = set()
......@@ -115,12 +116,12 @@ class ConstraintCSPProblem:
for t in types:
assert isinstance(t, str)
if len(types) == 1:
anded.add('%s == "%s"' % ('=='.join(vars), types[0]))
anded.add('%s == "%s"' % ("==".join(vars), types[0]))
else:
anded.add('%s in %s' % ('=='.join(vars), types))
anded.add("%s in %s" % ("==".join(vars), types))
for var in vars:
variables.add(var)
orred.add('(' + ' and '.join(list(anded)) + ')')
orred.add("(" + " and ".join(list(anded)) + ")")
expr = " or ".join(list(orred))
self.add_expr(tuple(variables), expr)
......@@ -131,12 +132,7 @@ _OR = 1
_EQ = 2
_EQV = 3
OPSYM = {
_AND: "and",
_OR: "or",
_EQ: "eq",
_EQV: "eqv"
}
OPSYM = {_AND: "and", _OR: "or", _EQ: "eq", _EQV: "eqv"}
class GecodeCSPProblem:
......@@ -161,13 +157,13 @@ class GecodeCSPProblem:
def __init__(self):
self.constraints = []
self.op = [_AND]
self.domains = {} # maps var name -> var value
self.variables = {} # maps var name -> var index
self.ivariables = [] # maps var index-> var name
self.values = {} # maps val name -> val index
self.domains = {} # maps var name -> var value
self.variables = {} # maps var name -> var index
self.ivariables = [] # maps var index-> var name
self.values = {} # maps val name -> val index
self.all_values = set() # this gets turned into a list later
self.idx_domains = [] # maps var index -> list of val index
self.ivalues = {} # only used for debugging
self.idx_domains = [] # maps var index -> list of val index
self.ivalues = {} # only used for debugging
def debug(self):
self.ivalues = {}
......@@ -180,17 +176,17 @@ class GecodeCSPProblem:
def pretty_print_ops(self, ops):
if ops[0] in (_AND, _OR):
res = [OPSYM[ops[0]], '(']