зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1652503: [mozlint] Use `attrs` for `mozlint.result.Issue`; r=ahal
This gives sorting on `Issue` for free, which makes it easier to write tests for linters. Differential Revision: https://phabricator.services.mozilla.com/D84643
This commit is contained in:
Родитель
89e3880e17
Коммит
97a298b135
|
@ -2,6 +2,8 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import attr
|
||||
|
||||
from ..result import Issue
|
||||
|
||||
|
||||
|
@ -25,7 +27,7 @@ class CompactFormatter(object):
|
|||
for err in errors:
|
||||
assert isinstance(err, Issue)
|
||||
|
||||
d = {s: getattr(err, s) for s in err.__slots__}
|
||||
d = attr.asdict(err)
|
||||
d["column"] = ", col %s" % d["column"] if d["column"] else ""
|
||||
d['level'] = d['level'].capitalize()
|
||||
d['rule'] = d['rule'] or d['linter']
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import attr
|
||||
|
||||
from ..result import Issue
|
||||
|
||||
|
||||
|
@ -20,7 +22,7 @@ class TreeherderFormatter(object):
|
|||
for err in errors:
|
||||
assert isinstance(err, Issue)
|
||||
|
||||
d = {s: getattr(err, s) for s in err.__slots__}
|
||||
d = attr.asdict(err)
|
||||
d["column"] = ":%s" % d["column"] if d["column"] else ""
|
||||
d['level'] = d['level'].upper()
|
||||
d['rule'] = d['rule'] or d['linter']
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import attr
|
||||
|
||||
from ..result import Issue
|
||||
|
||||
|
||||
|
@ -20,7 +22,7 @@ class UnixFormatter(object):
|
|||
for err in errors:
|
||||
assert isinstance(err, Issue)
|
||||
|
||||
slots = {s: getattr(err, s) for s in err.__slots__}
|
||||
slots = attr.asdict(err)
|
||||
slots["path"] = slots['relpath']
|
||||
slots["column"] = "%d:" % slots["column"] if slots["column"] else ""
|
||||
slots["rule"] = slots["rule"] or slots["linter"]
|
||||
|
|
|
@ -3,10 +3,12 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
from collections import defaultdict
|
||||
from json import dumps, JSONEncoder
|
||||
from json import JSONEncoder
|
||||
import os
|
||||
import mozpack.path as mozpath
|
||||
|
||||
import attr
|
||||
|
||||
|
||||
class ResultSummary(object):
|
||||
"""Represents overall result state from an entire lint run."""
|
||||
|
@ -55,6 +57,7 @@ class ResultSummary(object):
|
|||
self.suppressed_warnings[k] += v
|
||||
|
||||
|
||||
@attr.s(slots=True, kw_only=True)
|
||||
class Issue(object):
|
||||
"""Represents a single lint issue and its related metadata.
|
||||
|
||||
|
@ -72,62 +75,35 @@ class Issue(object):
|
|||
:param diff: a diff describing the changes that need to be made to the code
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
"linter",
|
||||
"path",
|
||||
"message",
|
||||
"lineno",
|
||||
"column",
|
||||
"hint",
|
||||
"source",
|
||||
"level",
|
||||
"rule",
|
||||
"lineoffset",
|
||||
"diff",
|
||||
"relpath",
|
||||
linter = attr.ib()
|
||||
path = attr.ib()
|
||||
lineno = attr.ib(
|
||||
default=None, converter=lambda lineno: int(lineno) if lineno else 0
|
||||
)
|
||||
column = attr.ib(
|
||||
default=None, converter=lambda column: int(column) if column else column
|
||||
)
|
||||
message = attr.ib()
|
||||
hint = attr.ib(default=None)
|
||||
source = attr.ib(default=None)
|
||||
level = attr.ib(default=None, converter=lambda level: level or "error")
|
||||
rule = attr.ib(default=None)
|
||||
lineoffset = attr.ib(default=None)
|
||||
diff = attr.ib(default=None)
|
||||
relpath = attr.ib(init=False, default=None)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
linter,
|
||||
path,
|
||||
message,
|
||||
lineno,
|
||||
column=None,
|
||||
hint=None,
|
||||
source=None,
|
||||
level=None,
|
||||
rule=None,
|
||||
lineoffset=None,
|
||||
diff=None,
|
||||
relpath=None,
|
||||
):
|
||||
self.message = message
|
||||
self.lineno = int(lineno) if lineno else 0
|
||||
self.column = int(column) if column else column
|
||||
self.hint = hint
|
||||
self.source = source
|
||||
self.level = level or "error"
|
||||
self.linter = linter
|
||||
self.rule = rule
|
||||
self.lineoffset = lineoffset
|
||||
self.diff = diff
|
||||
|
||||
def __attrs_post_init__(self):
|
||||
root = ResultSummary.root
|
||||
assert root is not None, 'Missing ResultSummary.root'
|
||||
if os.path.isabs(path):
|
||||
self.path = mozpath.normpath(path)
|
||||
if os.path.isabs(self.path):
|
||||
self.path = mozpath.normpath(self.path)
|
||||
if self.path.startswith(root):
|
||||
self.relpath = mozpath.relpath(self.path, root)
|
||||
else:
|
||||
self.relpath = self.path
|
||||
else:
|
||||
self.path = mozpath.join(root, path)
|
||||
self.relpath = mozpath.normpath(path)
|
||||
|
||||
def __repr__(self):
|
||||
s = dumps(self, cls=IssueEncoder, indent=2)
|
||||
return "Issue({})".format(s)
|
||||
self.relpath = mozpath.normpath(self.path)
|
||||
self.path = mozpath.join(root, self.path)
|
||||
|
||||
|
||||
class IssueEncoder(JSONEncoder):
|
||||
|
@ -140,7 +116,7 @@ class IssueEncoder(JSONEncoder):
|
|||
|
||||
def default(self, o):
|
||||
if isinstance(o, Issue):
|
||||
return {a: getattr(o, a) for a in o.__slots__}
|
||||
return attr.asdict(o)
|
||||
return JSONEncoder.default(self, o)
|
||||
|
||||
|
||||
|
@ -154,14 +130,15 @@ def from_config(config, **kwargs):
|
|||
:param kwargs: same as :class:`~result.Issue`
|
||||
:returns: :class:`~result.Issue` object
|
||||
"""
|
||||
attrs = {}
|
||||
for attr in Issue.__slots__:
|
||||
attrs[attr] = kwargs.get(attr, config.get(attr))
|
||||
args = {}
|
||||
for arg in attr.fields(Issue):
|
||||
if arg.init:
|
||||
args[arg.name] = kwargs.get(arg.name, config.get(arg.name))
|
||||
|
||||
if not attrs["linter"]:
|
||||
attrs["linter"] = config.get("name")
|
||||
if not args["linter"]:
|
||||
args["linter"] = config.get("name")
|
||||
|
||||
if not attrs["message"]:
|
||||
attrs["message"] = config.get("description")
|
||||
if not args["message"]:
|
||||
args["message"] = config.get("description")
|
||||
|
||||
return Issue(**attrs)
|
||||
return Issue(**args)
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
import json
|
||||
|
||||
import attr
|
||||
|
||||
import mozunit
|
||||
import mozpack.path as mozpath
|
||||
import pytest
|
||||
|
@ -121,7 +123,7 @@ def test_formatters(result, name):
|
|||
opts = EXPECTED[name]
|
||||
fmt = formatters.get(name, **opts["kwargs"])
|
||||
# encoding to str bypasses a UnicodeEncodeError in pytest
|
||||
assert fmt(result).encode("utf-8") == opts["format"].encode("utf-8")
|
||||
assert fmt(result) == opts["format"]
|
||||
|
||||
|
||||
def test_json_formatter(result):
|
||||
|
@ -130,10 +132,10 @@ def test_json_formatter(result):
|
|||
|
||||
assert set(formatted.keys()) == set(result.issues.keys())
|
||||
|
||||
slots = Issue.__slots__
|
||||
attrs = attr.fields(Issue)
|
||||
for errors in formatted.values():
|
||||
for err in errors:
|
||||
assert all(s in err for s in slots)
|
||||
assert all(a.name in err for a in attrs)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
@ -11,12 +11,12 @@ from mozlint.result import ResultSummary
|
|||
def test_issue_defaults():
|
||||
ResultSummary.root = '/fake/root'
|
||||
|
||||
issue = Issue('linter', 'path', 'message', None)
|
||||
issue = Issue(linter='linter', path='path', message='message', lineno=None)
|
||||
assert issue.lineno == 0
|
||||
assert issue.column is None
|
||||
assert issue.level == 'error'
|
||||
|
||||
issue = Issue('linter', 'path', 'message', '1', column='2')
|
||||
issue = Issue(linter='linter', path='path', message='message', lineno='1', column='2')
|
||||
assert issue.lineno == 1
|
||||
assert issue.column == 2
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче