diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 23d0301..6453114 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,6 +6,11 @@ default_stages: [commit] repos: - repo: local hooks: + - id: ruff + name: ruff + entry: ruff check + language: system + types_or: [python] - id: black name: black formatter diff --git a/consul/base.py b/consul/base.py old mode 100755 new mode 100644 index 9cf88fa..d548f99 --- a/consul/base.py +++ b/consul/base.py @@ -37,7 +37,6 @@ class BadRequest(ConsulException): class ClientError(ConsulException): """Encapsulates 4xx Http error code""" - pass # @@ -128,13 +127,18 @@ def _compat(self, script=None, interval=None, ttl=None, http=None, timeout=None, ret = {"check": {}} if script: - assert interval and not (ttl or http) + assert interval + assert not ttl + assert not http ret["check"] = {"script": script, "interval": interval} if ttl: - assert not (interval or script or http) + assert not interval or script + assert not http ret["check"] = {"ttl": ttl} if http: - assert interval and not (script or ttl) + assert interval + assert not script + assert not ttl ret["check"] = {"http": http, "interval": interval} if timeout: assert http @@ -1770,10 +1774,7 @@ def create(self, name=None, node=None, checks=None, lock_delay=15, behavior="rel if ttl: assert 10 <= ttl <= 86400 data["ttl"] = "%ss" % ttl - if data: - data = json.dumps(data) - else: - data = "" + data = json.dumps(data) if data else "" return self.agent.http.put(CB.json(is_id=True), "/v1/session/create", params=params, data=data) @@ -1988,10 +1989,7 @@ def create(self, name=None, type="client", rules=None, acl_id=None, token=None): if acl_id: payload["ID"] = acl_id - if payload: - data = json.dumps(payload) - else: - data = "" + data = json.dumps(payload) if payload else "" return self.agent.http.put(CB.json(is_id=True), "/v1/acl/create", params=params, data=data) @@ -2123,45 +2121,37 @@ def _query_data( ttl=None, regexp=None, ): - service_body = dict( - [ - (k, v) + service_body = { + k: v for k, v in { "service": service, "onlypassing": onlypassing, "tags": tags, - "failover": dict( - [ - (k, v) + "failover": { + k: v for k, v in {"nearestn": nearestn, "datacenters": datacenters}.items() if v is not None - ] - ), + }, }.items() if v is not None - ] - ) + } - data = dict( - [ - (k, v) + data = { + k: v for k, v in { "name": name, "session": session, "token": token or self.agent.token, "dns": {"ttl": ttl} if ttl is not None else None, - "template": dict( - [ - (k, v) + "template": { + k: v for k, v in {"type": "name_prefix_match", "regexp": regexp}.items() if v is not None - ] - ), + }, "service": service_body, }.items() if v is not None - ] - ) + } return json.dumps(data) def create( diff --git a/consul/twisted.py b/consul/twisted.py index 23ec9ed..4695993 100644 --- a/consul/twisted.py +++ b/consul/twisted.py @@ -32,7 +32,7 @@ def getContext(self, hostname, port): class HTTPClient(base.HTTPClient): def __init__(self, contextFactory, *args, **kwargs): super(HTTPClient, self).__init__(*args, **kwargs) - agent_kwargs = dict(reactor=reactor, pool=HTTPConnectionPool(reactor)) + agent_kwargs = {"reactor": reactor, "pool": HTTPConnectionPool(reactor)} if contextFactory is not None: # use the provided context factory agent_kwargs["contextFactory"] = contextFactory @@ -62,12 +62,10 @@ def compat_string(value): def _get_resp(self, response): # Merge multiple header values as per RFC2616 # http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2 - headers = dict( - [ - (self.compat_string(k), ",".join(map(self.compat_string, v))) + headers = { + self.compat_string(k): ",".join(map(self.compat_string, v)) for k, v in dict(response.headers.getAllRawHeaders()).items() - ] - ) + } body = yield response.text(encoding="utf-8") returnValue((response.code, headers, body)) diff --git a/docs/__init__.py b/docs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml index aa9f034..561d78b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,4 +8,69 @@ preview = true line_length = 120 case_sensitive = true profile = "black" -src_paths = ["."] \ No newline at end of file +src_paths = ["."] + +[tool.ruff] +target-version = 'py39' +# See complete list : https://beta.ruff.rs/docs/rules +select = [ +# "F", # pyflakes +# "E", # pycodestyle errors + "W", # pycodestyle warnings + "C90", # mccabe +# "N", # pep8-naming +# "UP", # pyupgrade + "YTT", # flake8-2020 + "CPY", # Copyright-related rules + "C4", # flake8-comprehensions + "T10", # flake8-debugger + "DJ", # flake8-django + "EXE", # flake8-executable + "FA", # flake8-future-annotations + "ISC", # flake8-implicit-str-concat + "ICN", # flake8-import-conventions + "INP", # flake8-no-pep420 + "PIE", # flake8-pie + "T20", # flake8-print +# "PYI", # flake8-pyi +# "PT", # flake8-pytest-style + "Q", # flake8-quotes + "RSE", # flake8-raise +# "SLF", # flake8-self + "SLOT", # flake8-slots +# "SIM", # flake8-simplify + "TID", # flake8-tidy-imports + "TCH", # flake8-type-checking + "INT", # flake8-gettext + "PD", # pandas-vet + "PLC", # pylint-convention + "PLE", # pylint-errors + "PLW", # pylint-warnings + "FLY", # flynt + "NPY", # numpy-specific rules + "AIR", # airflow +] + +fixable = [ + "PIE", + "PT", + "C4", + "SIM", + "SIM", + "UP", +] + +# Never enforce some rules +ignore = [ + "E501", # line length violations +] + +## Ignore some rules for some files +[tool.ruff.per-file-ignores] +"**/*test*.py" = [ + "SLF001", # Private member accessed + "T201", # print found +] + +[tool.ruff.mccabe] +max-complexity = 15 \ No newline at end of file diff --git a/tests-requirements.txt b/tests-requirements.txt index de6ca6f..a931d8b 100644 --- a/tests-requirements.txt +++ b/tests-requirements.txt @@ -6,6 +6,7 @@ pyOpenSSL pytest pytest-rerunfailures pytest-twisted +ruff tornado treq twisted \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/conftest.py b/tests/conftest.py index cfd2b84..3e2932e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -52,10 +52,7 @@ def start_consul_instance(acl_master_token=None): tmpdir.chdir() (system, node, release, version, machine, processor) = platform.uname() - if system == "Darwin": - postfix = "osx" - else: - postfix = "linux64" + postfix = "osx" if system == "Darwin" else "linux64" bin = os.path.join(os.path.dirname(__file__), "consul." + postfix) command = "{bin} agent -dev -bind=127.0.0.1 -config-dir=." command = command.format(bin=bin).strip() @@ -107,7 +104,7 @@ def consul_instance(): p.terminate() -@pytest.fixture +@pytest.fixture() def consul_port(consul_instance): port = consul_instance yield port @@ -122,7 +119,7 @@ def acl_consul_instance(): p.terminate() -@pytest.fixture +@pytest.fixture() def acl_consul(acl_consul_instance): ACLConsul = collections.namedtuple("ACLConsul", ["port", "token"]) port, token = acl_consul_instance diff --git a/tests/test_aio.py b/tests/test_aio.py index 1adfa0d..c4223bb 100644 --- a/tests/test_aio.py +++ b/tests/test_aio.py @@ -10,7 +10,7 @@ Check = consul.Check -@pytest.fixture +@pytest.fixture() def loop(request): asyncio.set_event_loop(None) loop = asyncio.new_event_loop() diff --git a/tests/test_base.py b/tests/test_base.py old mode 100755 new mode 100644 index 867d1fe..7b8a678 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -134,7 +134,7 @@ def test_status_200_passes(self): CB._status(response) @pytest.mark.parametrize( - "response, expected_exception", + ("response", "expected_exception"), [ (Response(400, None, None), consul.base.BadRequest), (Response(401, None, None), consul.base.ACLDisabled), @@ -177,7 +177,7 @@ class TestChecks: """ @pytest.mark.parametrize( - "url, interval, timeout, deregister, header, want", + ("url", "interval", "timeout", "deregister", "header", "want"), [ ( "http://example.com", @@ -248,7 +248,7 @@ def test_http_check(self, url, interval, timeout, deregister, header, want): assert ch == want @pytest.mark.parametrize( - "host, port, interval, timeout, deregister, want", + ("host", "port", "interval", "timeout", "deregister", "want"), [ ( "localhost", @@ -305,7 +305,7 @@ def test_tcp_check(self, host, port, interval, timeout, deregister, want): assert ch == want @pytest.mark.parametrize( - "container_id, shell, script, interval, deregister, want", + ("container_id", "shell", "script", "interval", "deregister", "want"), [ ( "wandering_bose", diff --git a/tests/test_std.py b/tests/test_std.py index 64bf4be..780cf4e 100644 --- a/tests/test_std.py +++ b/tests/test_std.py @@ -187,9 +187,9 @@ def test_agent_checks(self, consul_port): c = consul.Consul(port=consul_port) def verify_and_dereg_check(check_id): - assert set(c.agent.checks().keys()) == set([check_id]) + assert set(c.agent.checks().keys()) == {check_id} assert c.agent.check.deregister(check_id) is True - assert set(c.agent.checks().keys()) == set([]) + assert set(c.agent.checks().keys()) == set() def verify_check_status(check_id, status, notes=None): checks = c.agent.checks() @@ -202,7 +202,7 @@ def verify_check_status(check_id, status, notes=None): assert c.agent.checks()["check"]["Notes"] == "foo" c.agent.check.deregister("check") - assert set(c.agent.checks().keys()) == set([]) + assert set(c.agent.checks().keys()) == set() assert c.agent.check.register("script_check", Check.script("/bin/true", 10)) is True verify_and_dereg_check("script_check") @@ -272,8 +272,8 @@ def test_agent_checks_service_id(self, consul_port): time.sleep(40 / 1000.0) index, nodes = c.health.service("foo1") - assert set([check["ServiceID"] for node in nodes for check in node["Checks"]]) == set(["foo1", ""]) - assert set([check["CheckID"] for node in nodes for check in node["Checks"]]) == set(["foo", "serfHealth"]) + assert {check["ServiceID"] for node in nodes for check in node["Checks"]} == {"foo1", ""} + assert {check["CheckID"] for node in nodes for check in node["Checks"]} == {"foo", "serfHealth"} # Clean up tasks assert c.agent.check.deregister("foo") is True @@ -325,15 +325,15 @@ def test_agent_service_maintenance(self, consul_port): time.sleep(40 / 1000.0) checks_pre = c.agent.checks() - assert "_service_maintenance:foo" in checks_pre.keys() - assert "test" == checks_pre["_service_maintenance:foo"]["Notes"] + assert "_service_maintenance:foo" in checks_pre + assert checks_pre["_service_maintenance:foo"]["Notes"] == "test" c.agent.service.maintenance("foo", "false") time.sleep(40 / 1000.0) checks_post = c.agent.checks() - assert "_service_maintenance:foo" not in checks_post.keys() + assert "_service_maintenance:foo" not in checks_post # Cleanup c.agent.service.deregister("foo") @@ -348,15 +348,15 @@ def test_agent_node_maintenance(self, consul_port): time.sleep(40 / 1000.0) checks_pre = c.agent.checks() - assert "_node_maintenance" in checks_pre.keys() - assert "test" == checks_pre["_node_maintenance"]["Notes"] + assert "_node_maintenance" in checks_pre + assert checks_pre["_node_maintenance"]["Notes"] == "test" c.agent.maintenance("false") time.sleep(40 / 1000.0) checks_post = c.agent.checks() - assert "_node_maintenance" not in checks_post.keys() + assert "_node_maintenance" not in checks_post def test_agent_members(self, consul_port): c = consul.Consul(port=consul_port) @@ -373,12 +373,12 @@ def test_agent_members(self, consul_port): def test_agent_self(self, consul_port): c = consul.Consul(port=consul_port) - assert set(c.agent.self().keys()) == set(["Member", "Stats", "Config", "Coord", "DebugConfig", "Meta"]) + assert set(c.agent.self().keys()) == {"Member", "Stats", "Config", "Coord", "DebugConfig", "Meta"} def test_agent_services(self, consul_port): c = consul.Consul(port=consul_port) assert c.agent.service.register("foo") is True - assert set(c.agent.services().keys()) == set(["foo"]) + assert set(c.agent.services().keys()) == {"foo"} assert c.agent.service.deregister("foo") is True assert set(c.agent.services().keys()) == set() @@ -419,16 +419,16 @@ def test_catalog(self, consul_port): # test catalog.node pytest.raises(consul.ConsulException, c.catalog.node, "n1", dc="dc2") _, node = c.catalog.node("n1") - assert set(node["Services"].keys()) == set(["s1", "s2"]) + assert set(node["Services"].keys()) == {"s1", "s2"} _, node = c.catalog.node("n3") assert node is None # test catalog.service pytest.raises(consul.ConsulException, c.catalog.service, "s1", dc="dc2") _, nodes = c.catalog.service("s1") - assert set([x["Node"] for x in nodes]) == set(["n1", "n2"]) + assert {x["Node"] for x in nodes} == {"n1", "n2"} _, nodes = c.catalog.service("s1", tag="master") - assert set([x["Node"] for x in nodes]) == set(["n2"]) + assert {x["Node"] for x in nodes} == {"n2"} # test catalog.deregister pytest.raises(consul.ConsulException, c.catalog.deregister, "n2", dc="dc2") @@ -440,7 +440,7 @@ def test_catalog(self, consul_port): assert [x["Node"] for x in nodes] == ["n1", "n2"] # check n2's s1 service was removed though _, nodes = c.catalog.service("s1") - assert set([x["Node"] for x in nodes]) == set(["n1"]) + assert {x["Node"] for x in nodes} == {"n1"} # cleanup assert c.catalog.deregister("n1") is True @@ -525,7 +525,7 @@ def test_health_state(self, consul_port): # check the nodes show for the /health/state/any endpoint index, nodes = c.health.state("any") - assert set([node["ServiceID"] for node in nodes]) == set(["", "foo:1", "foo:2"]) + assert {node["ServiceID"] for node in nodes} == {"", "foo:1", "foo:2"} # but that they aren't passing their health check index, nodes = c.health.state("passing") @@ -539,14 +539,14 @@ def test_health_state(self, consul_port): # both nodes are now available index, nodes = c.health.state("passing") - assert set([node["ServiceID"] for node in nodes]) == set(["", "foo:1", "foo:2"]) + assert {node["ServiceID"] for node in nodes} == {"", "foo:1", "foo:2"} # wait until the short ttl node fails time.sleep(2200 / 1000.0) # only one node available index, nodes = c.health.state("passing") - assert set([node["ServiceID"] for node in nodes]) == set(["", "foo:1"]) + assert {node["ServiceID"] for node in nodes} == {"", "foo:1"} # ping the failed node's health check c.agent.check.ttl_pass("service:foo:2") @@ -555,7 +555,7 @@ def test_health_state(self, consul_port): # check both nodes are available index, nodes = c.health.state("passing") - assert set([node["ServiceID"] for node in nodes]) == set(["", "foo:1", "foo:2"]) + assert {node["ServiceID"] for node in nodes} == {"", "foo:1", "foo:2"} # deregister the nodes c.agent.service.deregister("foo:1") @@ -667,7 +667,7 @@ def test_acl_explict_token_use(self, acl_consul): master_token = acl_consul.token acls = c.acl.list(token=master_token) - assert set([x["ID"] for x in acls]) == set(["anonymous", master_token]) + assert {x["ID"] for x in acls} == {"anonymous", master_token} assert c.acl.info("1" * 36) is None compare = [c.acl.info(master_token), c.acl.info("anonymous")] @@ -727,7 +727,7 @@ def test_acl_explict_token_use(self, acl_consul): assert c.agent.service.deregister("foo-1") is True c.acl.destroy(token, token=master_token) acls = c.acl.list(token=master_token) - assert set([x["ID"] for x in acls]) == set(["anonymous", master_token]) + assert {x["ID"] for x in acls} == {"anonymous", master_token} def test_acl_implicit_token_use(self, acl_consul): # configure client to use the master token by default @@ -735,7 +735,7 @@ def test_acl_implicit_token_use(self, acl_consul): master_token = acl_consul.token acls = c.acl.list() - assert set([x["ID"] for x in acls]) == set(["anonymous", master_token]) + assert {x["ID"] for x in acls} == {"anonymous", master_token} assert c.acl.info("foo") is None compare = [c.acl.info(master_token), c.acl.info("anonymous")] @@ -783,7 +783,7 @@ def test_acl_implicit_token_use(self, acl_consul): # clean up c.acl.destroy(token) acls = c.acl.list() - assert set([x["ID"] for x in acls]) == set(["anonymous", master_token]) + assert {x["ID"] for x in acls} == {"anonymous", master_token} def test_status_leader(self, consul_port): c = consul.Consul(port=consul_port) @@ -817,12 +817,16 @@ def test_query(self, consul_port): query = c.query.create(query_service, query_name) # assert response contains query ID - assert "ID" in query and query["ID"] is not None and str(query["ID"]) != "" + assert "ID" in query + assert query["ID"] is not None + assert str(query["ID"]) != "" # retrieve query using id and name queries = c.query.get(query["ID"]) - assert queries != [] and len(queries) == 1 - assert queries[0]["Name"] == query_name and queries[0]["ID"] == query["ID"] + assert queries != [] + assert len(queries) == 1 + assert queries[0]["Name"] == query_name + assert queries[0]["ID"] == query["ID"] # explain query assert c.query.explain(query_name)["Query"] @@ -834,7 +838,7 @@ def test_coordinate(self, consul_port): c = consul.Consul(port=consul_port) c.coordinate.nodes() c.coordinate.datacenters() - assert set(c.coordinate.datacenters()[0].keys()) == set(["Datacenter", "Coordinates", "AreaID"]) + assert set(c.coordinate.datacenters()[0].keys()) == {"Datacenter", "Coordinates", "AreaID"} def test_operator(self, consul_port): c = consul.Consul(port=consul_port) diff --git a/tests/test_tornado.py b/tests/test_tornado.py index a5dda3b..cea9c5f 100644 --- a/tests/test_tornado.py +++ b/tests/test_tornado.py @@ -11,7 +11,7 @@ Check = consul.Check -@pytest.fixture +@pytest.fixture() def loop(): loop = ioloop.IOLoop() loop.make_current() diff --git a/tox.ini b/tox.ini index 928ffad..7b75a75 100644 --- a/tox.ini +++ b/tox.ini @@ -1,9 +1,5 @@ [tox] -envlist = flake8,lint,pypy3,py35,py36,py37,py38 - -[flake8] -ignore = F811,E226 -exclude = .tox/*,xx/*,__*,docs +envlist = lint,pypy3,py35,py36,py37,py38 [testenv] deps = @@ -11,10 +7,6 @@ deps = commands = py.test --reruns=3 {posargs:consul tests} -[testenv:flake8] -deps = flake8 -commands = flake8 - # Linter environment [testenv:lint] deps = -r tests-requirements.txt