9 Commits 745caa0c0a ... 43ce2c0d98

Author SHA1 Message Date
  Fabian Peter Hammerle 43ce2c0d98 added unit test for backlight_eval 2 weeks ago
  Fabian Peter Hammerle 36d947e22b added argument & return type hints 2 weeks ago
  Fabian Peter Hammerle 3e21c5a774 added unit tests for Backlight class 2 weeks ago
  Fabian Peter Hammerle b3b0c1cf59 pipeline: run pylint 2 weeks ago
  Fabian Peter Hammerle 289980b1a6 pipeline: run mypy 2 weeks ago
  Fabian Peter Hammerle a6257d32af pipeline: check test coverage 2 weeks ago
  Fabian Peter Hammerle 8176a81edb format code via black 2 weeks ago
  Fabian Peter Hammerle 38975f7f59 pipeline env: explicitly add importlib_metadata to be able to lock with python3.8 2 weeks ago
  Fabian Peter Hammerle 6145e6ae2a format code via black 2 weeks ago
11 changed files with 473 additions and 88 deletions
  1. 33 2
      .github/workflows/python.yml
  2. 2 0
      .gitignore
  3. 14 1
      Pipfile
  4. 257 23
      Pipfile.lock
  5. 2 0
      README.md
  6. 26 23
      acpi_backlight/__init__.py
  7. 5 6
      acpi_backlight/evaluate.py
  8. 2 0
      mypy.ini
  9. 12 10
      setup.py
  10. 91 0
      test/test_backlight.py
  11. 29 23
      tests/test_evaluate.py

+ 33 - 2
.github/workflows/python.yml

@@ -11,6 +11,23 @@ on:
   - cron: '0 20 * * 5'
 
 jobs:
+  code-format:
+    runs-on: ubuntu-18.04
+    strategy:
+      matrix:
+        python-version:
+        - 3.8
+    steps:
+    - uses: actions/checkout@v1
+    - uses: actions/setup-python@v1
+      with:
+        python-version: ${{ matrix.python-version }}
+    - run: pip install --upgrade pipenv==2020.8.13
+    - run: pipenv install --python "$PYTHON_VERSION" --deploy --dev
+      env:
+        PYTHON_VERSION: ${{ matrix.python-version }}
+    - run: pipenv graph
+    - run: pipenv run black --check .
   tests:
     runs-on: ubuntu-18.04
     strategy:
@@ -26,9 +43,23 @@ jobs:
     - uses: actions/setup-python@v1
       with:
         python-version: ${{ matrix.python-version }}
-    - run: pip install --upgrade pipenv==2020.6.2
+    - run: pip install --upgrade pipenv==2020.8.13
     - run: pipenv install --python "$PYTHON_VERSION" --deploy --dev
       env:
         PYTHON_VERSION: ${{ matrix.python-version }}
     - run: pipenv graph
-    - run: pipenv run pytest
+    - run: pipenv run pytest --cov=acpi_backlight --cov-report=term-missing --cov-fail-under=90
+    - run: pipenv run pylint --load-plugins=pylint_import_requirements acpi_backlight
+    # https://github.com/PyCQA/pylint/issues/352
+    - run: pipenv run pylint tests/*
+    - run: pipenv run mypy acpi_backlight tests
+    # >=1.9.0 to detect branch name
+    # https://github.com/coveralls-clients/coveralls-python/pull/207
+    # https://github.com/coverallsapp/github-action/issues/4#issuecomment-547036866
+    # 1.11.0 https://github.com/coveralls-clients/coveralls-python/issues/219
+    - run: pip install 'coveralls>=1.9.0,<2,!=1.11.0'
+    # https://github.com/coverallsapp/github-action/issues/30
+    # https://github.com/coverallsapp/github-action/issues/4#issuecomment-529399410
+    - run: coveralls
+      env:
+        COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}

+ 2 - 0
.gitignore

@@ -1,3 +1,5 @@
+/.coverage
 /.eggs/
+/.mypy_cache/
 /build/
 /dist/

+ 14 - 1
Pipfile

@@ -7,10 +7,23 @@ name = "pypi"
 acpi-backlight = {editable = true, path = "."}
 
 [dev-packages]
+# black requires python>=3.6
+# https://github.com/psf/black/commit/e74117f172e29e8a980e2c9de929ad50d3769150#diff-2eeaed663bd0d25b7e608891384b7298R51
+black = {version = "==20.8b1", markers = "python_version >= '3.6'"}
+mypy = "*"
+pylint = "*"
+pylint-import-requirements = "*"
 pytest = "*"
+pytest-cov = "*"
 
+# python3.5 compatibility
+isort = "<5"
 # workaround https://github.com/pytest-dev/pytest/issues/3953
 pathlib2 = {version = "*", markers="python_version < '3.6'"}
-# zipp v2.0.0 dropped support for python3.5
 # https://github.com/jaraco/zipp/commit/05a3c52b4d41690e0471a2e283cffb500dc0329a
 zipp = "<2"
+
+[requires]
+python_version = "3"
+
+# Pipfile syntax: https://github.com/pypa/pipfile#pipfile

+ 257 - 23
Pipfile.lock

@@ -1,10 +1,12 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "90ead5fbcddf0ef730c9398cc1df5a5f169e67ea8afdc61eb41bcc2a8942f3ed"
+            "sha256": "d28f4118d88e4a515756cfa9768faaef7dfcb3ec7fc2c98ae70dc643776c7c81"
         },
         "pipfile-spec": 6,
-        "requires": {},
+        "requires": {
+            "python_version": "3"
+        },
         "sources": [
             {
                 "name": "pypi",
@@ -20,27 +22,162 @@
         }
     },
     "develop": {
+        "appdirs": {
+            "hashes": [
+                "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41",
+                "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"
+            ],
+            "version": "==1.4.4"
+        },
+        "astroid": {
+            "hashes": [
+                "sha256:2f4078c2a41bf377eea06d71c9d2ba4eb8f6b1af2135bec27bbbb7d8f12bb703",
+                "sha256:bc58d83eb610252fd8de6363e39d4f1d0619c894b0ed24603b881c02e64c7386"
+            ],
+            "version": "==2.4.2"
+        },
         "attrs": {
             "hashes": [
-                "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c",
-                "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"
+                "sha256:26b54ddbbb9ee1d34d5d3668dd37d6cf74990ab23c828c2888dccdceee395594",
+                "sha256:fce7fc47dfc976152e82d53ff92fa0407700c21acd20886a13777a0d20e655dc"
+            ],
+            "version": "==20.2.0"
+        },
+        "black": {
+            "hashes": [
+                "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"
+            ],
+            "index": "pypi",
+            "markers": "python_version >= '3.6'",
+            "version": "==20.8b1"
+        },
+        "click": {
+            "hashes": [
+                "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
+                "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
             ],
-            "version": "==19.3.0"
+            "version": "==7.1.2"
+        },
+        "coverage": {
+            "hashes": [
+                "sha256:0203acd33d2298e19b57451ebb0bed0ab0c602e5cf5a818591b4918b1f97d516",
+                "sha256:0f313707cdecd5cd3e217fc68c78a960b616604b559e9ea60cc16795c4304259",
+                "sha256:1c6703094c81fa55b816f5ae542c6ffc625fec769f22b053adb42ad712d086c9",
+                "sha256:1d44bb3a652fed01f1f2c10d5477956116e9b391320c94d36c6bf13b088a1097",
+                "sha256:280baa8ec489c4f542f8940f9c4c2181f0306a8ee1a54eceba071a449fb870a0",
+                "sha256:29a6272fec10623fcbe158fdf9abc7a5fa032048ac1d8631f14b50fbfc10d17f",
+                "sha256:2b31f46bf7b31e6aa690d4c7a3d51bb262438c6dcb0d528adde446531d0d3bb7",
+                "sha256:2d43af2be93ffbad25dd959899b5b809618a496926146ce98ee0b23683f8c51c",
+                "sha256:381ead10b9b9af5f64646cd27107fb27b614ee7040bb1226f9c07ba96625cbb5",
+                "sha256:47a11bdbd8ada9b7ee628596f9d97fbd3851bd9999d398e9436bd67376dbece7",
+                "sha256:4d6a42744139a7fa5b46a264874a781e8694bb32f1d76d8137b68138686f1729",
+                "sha256:50691e744714856f03a86df3e2bff847c2acede4c191f9a1da38f088df342978",
+                "sha256:530cc8aaf11cc2ac7430f3614b04645662ef20c348dce4167c22d99bec3480e9",
+                "sha256:582ddfbe712025448206a5bc45855d16c2e491c2dd102ee9a2841418ac1c629f",
+                "sha256:63808c30b41f3bbf65e29f7280bf793c79f54fb807057de7e5238ffc7cc4d7b9",
+                "sha256:71b69bd716698fa62cd97137d6f2fdf49f534decb23a2c6fc80813e8b7be6822",
+                "sha256:7858847f2d84bf6e64c7f66498e851c54de8ea06a6f96a32a1d192d846734418",
+                "sha256:78e93cc3571fd928a39c0b26767c986188a4118edc67bc0695bc7a284da22e82",
+                "sha256:7f43286f13d91a34fadf61ae252a51a130223c52bfefb50310d5b2deb062cf0f",
+                "sha256:86e9f8cd4b0cdd57b4ae71a9c186717daa4c5a99f3238a8723f416256e0b064d",
+                "sha256:8f264ba2701b8c9f815b272ad568d555ef98dfe1576802ab3149c3629a9f2221",
+                "sha256:9342dd70a1e151684727c9c91ea003b2fb33523bf19385d4554f7897ca0141d4",
+                "sha256:9361de40701666b034c59ad9e317bae95c973b9ff92513dd0eced11c6adf2e21",
+                "sha256:9669179786254a2e7e57f0ecf224e978471491d660aaca833f845b72a2df3709",
+                "sha256:aac1ba0a253e17889550ddb1b60a2063f7474155465577caa2a3b131224cfd54",
+                "sha256:aef72eae10b5e3116bac6957de1df4d75909fc76d1499a53fb6387434b6bcd8d",
+                "sha256:bd3166bb3b111e76a4f8e2980fa1addf2920a4ca9b2b8ca36a3bc3dedc618270",
+                "sha256:c1b78fb9700fc961f53386ad2fd86d87091e06ede5d118b8a50dea285a071c24",
+                "sha256:c3888a051226e676e383de03bf49eb633cd39fc829516e5334e69b8d81aae751",
+                "sha256:c5f17ad25d2c1286436761b462e22b5020d83316f8e8fcb5deb2b3151f8f1d3a",
+                "sha256:c851b35fc078389bc16b915a0a7c1d5923e12e2c5aeec58c52f4aa8085ac8237",
+                "sha256:cb7df71de0af56000115eafd000b867d1261f786b5eebd88a0ca6360cccfaca7",
+                "sha256:cedb2f9e1f990918ea061f28a0f0077a07702e3819602d3507e2ff98c8d20636",
+                "sha256:e8caf961e1b1a945db76f1b5fa9c91498d15f545ac0ababbe575cfab185d3bd8"
+            ],
+            "version": "==5.3"
         },
         "importlib-metadata": {
             "hashes": [
-                "sha256:0505dd08068cfec00f53a74a0ad927676d7757da81b7436a6eefe4c7cf75c545",
-                "sha256:15ec6c0fd909e893e3a08b3a7c76ecb149122fb14b7efe1199ddd4c7c57ea958"
+                "sha256:77a540690e24b0305878c37ffd421785a6f7e53c8b5720d211b211de8d0e95da",
+                "sha256:cefa1a2f919b866c5beb7c9f7b0ebb4061f30a8a9bf16d609b000e2dfaceb9c3"
+            ],
+            "version": "==2.0.0"
+        },
+        "iniconfig": {
+            "hashes": [
+                "sha256:80cf40c597eb564e86346103f609d74efce0f6b4d4f30ec8ce9e2c26411ba437",
+                "sha256:e5f92f89355a67de0595932a6c6c02ab4afddc6fcdc0bfc5becd0d60884d3f69"
             ],
-            "markers": "python_version < '3.8'",
-            "version": "==1.6.1"
+            "version": "==1.0.1"
+        },
+        "isort": {
+            "hashes": [
+                "sha256:54da7e92468955c4fceacd0c86bd0ec997b0e1ee80d97f67c35a78b719dccab1",
+                "sha256:6e811fcb295968434526407adb8796944f1988c5b65e8139058f2014cbe100fd"
+            ],
+            "index": "pypi",
+            "version": "==4.3.21"
         },
-        "more-itertools": {
+        "lazy-object-proxy": {
             "hashes": [
-                "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5",
-                "sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2"
+                "sha256:0c4b206227a8097f05c4dbdd323c50edf81f15db3b8dc064d08c62d37e1a504d",
+                "sha256:194d092e6f246b906e8f70884e620e459fc54db3259e60cf69a4d66c3fda3449",
+                "sha256:1be7e4c9f96948003609aa6c974ae59830a6baecc5376c25c92d7d697e684c08",
+                "sha256:4677f594e474c91da97f489fea5b7daa17b5517190899cf213697e48d3902f5a",
+                "sha256:48dab84ebd4831077b150572aec802f303117c8cc5c871e182447281ebf3ac50",
+                "sha256:5541cada25cd173702dbd99f8e22434105456314462326f06dba3e180f203dfd",
+                "sha256:59f79fef100b09564bc2df42ea2d8d21a64fdcda64979c0fa3db7bdaabaf6239",
+                "sha256:8d859b89baf8ef7f8bc6b00aa20316483d67f0b1cbf422f5b4dc56701c8f2ffb",
+                "sha256:9254f4358b9b541e3441b007a0ea0764b9d056afdeafc1a5569eee1cc6c1b9ea",
+                "sha256:9651375199045a358eb6741df3e02a651e0330be090b3bc79f6d0de31a80ec3e",
+                "sha256:97bb5884f6f1cdce0099f86b907aa41c970c3c672ac8b9c8352789e103cf3156",
+                "sha256:9b15f3f4c0f35727d3a0fba4b770b3c4ebbb1fa907dbcc046a1d2799f3edd142",
+                "sha256:a2238e9d1bb71a56cd710611a1614d1194dc10a175c1e08d75e1a7bcc250d442",
+                "sha256:a6ae12d08c0bf9909ce12385803a543bfe99b95fe01e752536a60af2b7797c62",
+                "sha256:ca0a928a3ddbc5725be2dd1cf895ec0a254798915fb3a36af0964a0a4149e3db",
+                "sha256:cb2c7c57005a6804ab66f106ceb8482da55f5314b7fcb06551db1edae4ad1531",
+                "sha256:d74bb8693bf9cf75ac3b47a54d716bbb1a92648d5f781fc799347cfc95952383",
+                "sha256:d945239a5639b3ff35b70a88c5f2f491913eb94871780ebfabb2568bd58afc5a",
+                "sha256:eba7011090323c1dadf18b3b689845fd96a61ba0a1dfbd7f24b921398affc357",
+                "sha256:efa1909120ce98bbb3777e8b6f92237f5d5c8ea6758efea36a473e1d38f7d3e4",
+                "sha256:f3900e8a5de27447acbf900b4750b0ddfd7ec1ea7fbaf11dfa911141bc522af0"
             ],
-            "version": "==8.4.0"
+            "version": "==1.4.3"
+        },
+        "mccabe": {
+            "hashes": [
+                "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
+                "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
+            ],
+            "version": "==0.6.1"
+        },
+        "mypy": {
+            "hashes": [
+                "sha256:2c6cde8aa3426c1682d35190b59b71f661237d74b053822ea3d748e2c9578a7c",
+                "sha256:3fdda71c067d3ddfb21da4b80e2686b71e9e5c72cca65fa216d207a358827f86",
+                "sha256:5dd13ff1f2a97f94540fd37a49e5d255950ebcdf446fb597463a40d0df3fac8b",
+                "sha256:6731603dfe0ce4352c555c6284c6db0dc935b685e9ce2e4cf220abe1e14386fd",
+                "sha256:6bb93479caa6619d21d6e7160c552c1193f6952f0668cdda2f851156e85186fc",
+                "sha256:81c7908b94239c4010e16642c9102bfc958ab14e36048fa77d0be3289dda76ea",
+                "sha256:9c7a9a7ceb2871ba4bac1cf7217a7dd9ccd44c27c2950edbc6dc08530f32ad4e",
+                "sha256:a4a2cbcfc4cbf45cd126f531dedda8485671545b43107ded25ce952aac6fb308",
+                "sha256:b7fbfabdbcc78c4f6fc4712544b9b0d6bf171069c6e0e3cb82440dd10ced3406",
+                "sha256:c05b9e4fb1d8a41d41dec8786c94f3b95d3c5f528298d769eb8e73d293abc48d",
+                "sha256:d7df6eddb6054d21ca4d3c6249cae5578cb4602951fd2b6ee2f5510ffb098707",
+                "sha256:e0b61738ab504e656d1fe4ff0c0601387a5489ca122d55390ade31f9ca0e252d",
+                "sha256:eff7d4a85e9eea55afa34888dfeaccde99e7520b51f867ac28a48492c0b1130c",
+                "sha256:f05644db6779387ccdb468cc47a44b4356fc2ffa9287135d05b70a98dc83b89a"
+            ],
+            "index": "pypi",
+            "version": "==0.782"
+        },
+        "mypy-extensions": {
+            "hashes": [
+                "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
+                "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"
+            ],
+            "version": "==0.4.3"
         },
         "packaging": {
             "hashes": [
@@ -58,6 +195,13 @@
             "markers": "python_version < '3.6'",
             "version": "==2.3.5"
         },
+        "pathspec": {
+            "hashes": [
+                "sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0",
+                "sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061"
+            ],
+            "version": "==0.8.0"
+        },
         "pluggy": {
             "hashes": [
                 "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
@@ -67,10 +211,26 @@
         },
         "py": {
             "hashes": [
-                "sha256:a673fa23d7000440cc885c17dbd34fafcb7d7a6e230b29f6766400de36a33c44",
-                "sha256:f3b3a4c36512a4c4f024041ab51866f11761cc169670204b235f6b20523d4e6b"
+                "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2",
+                "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"
             ],
-            "version": "==1.8.2"
+            "version": "==1.9.0"
+        },
+        "pylint": {
+            "hashes": [
+                "sha256:bb4a908c9dadbc3aac18860550e870f58e1a02c9f2c204fdf5693d73be061210",
+                "sha256:bfe68f020f8a0fece830a22dd4d5dddb4ecc6137db04face4c3420a46a52239f"
+            ],
+            "index": "pypi",
+            "version": "==2.6.0"
+        },
+        "pylint-import-requirements": {
+            "hashes": [
+                "sha256:39d0993baa0991c2c6258d310aacd5f69c850ba59cb3d2ae8aa69886886b7135",
+                "sha256:bc138263bf1f79d8d1bf3d3f6786248024a71b4202cd19a189f1346f3e5b20a7"
+            ],
+            "index": "pypi",
+            "version": "==2.0.5"
         },
         "pyparsing": {
             "hashes": [
@@ -81,11 +241,45 @@
         },
         "pytest": {
             "hashes": [
-                "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1",
-                "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"
+                "sha256:1cd09785c0a50f9af72220dd12aa78cfa49cbffc356c61eab009ca189e018a33",
+                "sha256:d010e24666435b39a4cf48740b039885642b6c273a3f77be3e7e03554d2806b7"
+            ],
+            "index": "pypi",
+            "version": "==6.1.0"
+        },
+        "pytest-cov": {
+            "hashes": [
+                "sha256:45ec2d5182f89a81fc3eb29e3d1ed3113b9e9a873bcddb2a71faaab066110191",
+                "sha256:47bd0ce14056fdd79f93e1713f88fad7bdcc583dcd7783da86ef2f085a0bb88e"
             ],
             "index": "pypi",
-            "version": "==5.4.3"
+            "version": "==2.10.1"
+        },
+        "regex": {
+            "hashes": [
+                "sha256:088afc8c63e7bd187a3c70a94b9e50ab3f17e1d3f52a32750b5b77dbe99ef5ef",
+                "sha256:1fe0a41437bbd06063aa184c34804efa886bcc128222e9916310c92cd54c3b4c",
+                "sha256:41bb65f54bba392643557e617316d0d899ed5b4946dccee1cb6696152b29844b",
+                "sha256:4318d56bccfe7d43e5addb272406ade7a2274da4b70eb15922a071c58ab0108c",
+                "sha256:4707f3695b34335afdfb09be3802c87fa0bc27030471dbc082f815f23688bc63",
+                "sha256:5533a959a1748a5c042a6da71fe9267a908e21eded7a4f373efd23a2cbdb0ecc",
+                "sha256:5f18875ac23d9aa2f060838e8b79093e8bb2313dbaaa9f54c6d8e52a5df097be",
+                "sha256:60b0e9e6dc45683e569ec37c55ac20c582973841927a85f2d8a7d20ee80216ab",
+                "sha256:84e9407db1b2eb368b7ecc283121b5e592c9aaedbe8c78b1a2f1102eb2e21d19",
+                "sha256:8d69cef61fa50c8133382e61fd97439de1ae623fe943578e477e76a9d9471637",
+                "sha256:9a02d0ae31d35e1ec12a4ea4d4cca990800f66a917d0fb997b20fbc13f5321fc",
+                "sha256:9bc13e0d20b97ffb07821aa3e113f9998e84994fe4d159ffa3d3a9d1b805043b",
+                "sha256:a6f32aea4260dfe0e55dc9733ea162ea38f0ea86aa7d0f77b15beac5bf7b369d",
+                "sha256:ae91972f8ac958039920ef6e8769277c084971a142ce2b660691793ae44aae6b",
+                "sha256:c570f6fa14b9c4c8a4924aaad354652366577b4f98213cf76305067144f7b100",
+                "sha256:d23a18037313714fb3bb5a94434d3151ee4300bae631894b1ac08111abeaa4a3",
+                "sha256:eaf548d117b6737df379fdd53bdde4f08870e66d7ea653e230477f071f861121",
+                "sha256:ebbe29186a3d9b0c591e71b7393f1ae08c83cb2d8e517d2a822b8f7ec99dfd8b",
+                "sha256:eda4771e0ace7f67f58bc5b560e27fb20f32a148cbc993b0c3835970935c2707",
+                "sha256:f1b3afc574a3db3b25c89161059d857bd4909a1269b0b3cb3c904677c8c4a3f7",
+                "sha256:f2388013e68e750eaa16ccbea62d4130180c26abb1d8e5d584b9baf69672b30f"
+            ],
+            "version": "==2020.9.27"
         },
         "six": {
             "hashes": [
@@ -94,12 +288,52 @@
             ],
             "version": "==1.15.0"
         },
-        "wcwidth": {
+        "toml": {
+            "hashes": [
+                "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
+                "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
+            ],
+            "version": "==0.10.1"
+        },
+        "typed-ast": {
+            "hashes": [
+                "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355",
+                "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919",
+                "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa",
+                "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652",
+                "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75",
+                "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01",
+                "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d",
+                "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1",
+                "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907",
+                "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c",
+                "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3",
+                "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b",
+                "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614",
+                "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb",
+                "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b",
+                "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41",
+                "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6",
+                "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34",
+                "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe",
+                "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4",
+                "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"
+            ],
+            "version": "==1.4.1"
+        },
+        "typing-extensions": {
+            "hashes": [
+                "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918",
+                "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c",
+                "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"
+            ],
+            "version": "==3.7.4.3"
+        },
+        "wrapt": {
             "hashes": [
-                "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784",
-                "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"
+                "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"
             ],
-            "version": "==0.2.5"
+            "version": "==1.12.1"
         },
         "zipp": {
             "hashes": [

+ 2 - 0
README.md

@@ -1,6 +1,8 @@
 # acpi-backlight
 
+[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
 [![CI Pipeline Status](https://github.com/fphammerle/acpi-backlight/workflows/tests/badge.svg)](https://github.com/fphammerle/acpi-backlight/actions)
+[![Coverage Status](https://coveralls.io/repos/github/fphammerle/acpi-backlight/badge.svg?branch=master)](https://coveralls.io/github/fphammerle/acpi-backlight?branch=master)
 
 ## setup
 

+ 26 - 23
acpi_backlight/__init__.py

@@ -5,61 +5,64 @@ import os
 
 import acpi_backlight.evaluate
 
-_ACPI_BACKLIGHT_ROOT_DIR_PATH = '/sys/class/backlight'
+_ACPI_BACKLIGHT_ROOT_DIR_PATH = "/sys/class/backlight"
 
 
 class Backlight:
 
-    def __init__(self, name='intel_backlight'):
+    # pylint: disable=too-few-public-methods; does not count properties
+
+    def __init__(self, name: str = "intel_backlight"):
         self._acpi_dir_path = os.path.join(_ACPI_BACKLIGHT_ROOT_DIR_PATH, name)
 
     @property
-    def _brightness_path(self):
-        return os.path.join(self._acpi_dir_path, 'brightness')
+    def _brightness_path(self) -> str:
+        return os.path.join(self._acpi_dir_path, "brightness")
 
     @property
-    def _max_brightness_path(self):
-        return os.path.join(self._acpi_dir_path, 'max_brightness')
+    def _max_brightness_path(self) -> str:
+        return os.path.join(self._acpi_dir_path, "max_brightness")
 
     @property
-    def _brightness_absolute(self):
-        with open(self._brightness_path, 'r') as brightness_file:
+    def _brightness_absolute(self) -> int:
+        with open(self._brightness_path, "r") as brightness_file:
             return int(brightness_file.read())
 
     @_brightness_absolute.setter
-    def _brightness_absolute(self, brightness_absolute):
-        with open(self._brightness_path, 'w') as brightness_file:
-            return brightness_file.write(str(round(brightness_absolute)))
+    def _brightness_absolute(self, brightness_absolute: int):
+        with open(self._brightness_path, "w") as brightness_file:
+            return brightness_file.write(str(brightness_absolute))
 
     @property
-    def _max_brightness_absolute(self):
-        with open(self._max_brightness_path, 'r') as max_brightness_file:
+    def _max_brightness_absolute(self) -> int:
+        with open(self._max_brightness_path, "r") as max_brightness_file:
             return int(max_brightness_file.read())
 
     @property
-    def brightness_relative(self):
+    def brightness_relative(self) -> float:
         return self._brightness_absolute / self._max_brightness_absolute
 
     @brightness_relative.setter
-    def brightness_relative(self, brightness_relative):
-        self._brightness_absolute = max(0, min(1, brightness_relative)) \
-            * self._max_brightness_absolute
+    def brightness_relative(self, brightness_relative: float) -> None:
+        self._brightness_absolute = round(
+            max(0, min(1, brightness_relative)) * self._max_brightness_absolute
+        )
 
 
-def backlight_eval(expr_str):
+def backlight_eval(expr_str: str) -> None:
     backlight = acpi_backlight.Backlight()
     backlight.brightness_relative = acpi_backlight.evaluate.evaluate_expression(
-        expr_str=expr_str,
-        names={'b': backlight.brightness_relative},
+        expr_str=expr_str, names={"b": backlight.brightness_relative}
     )
     print(backlight.brightness_relative)
 
 
-def main():
+def main() -> None:
     argparser = argparse.ArgumentParser()
-    argparser.add_argument('expr_str')
+    argparser.add_argument("expr_str")
     args = argparser.parse_args()
     backlight_eval(expr_str=args.expr_str)
 
-if __name__ == '__main__':
+
+if __name__ == "__main__":
     main()

+ 5 - 6
acpi_backlight/evaluate.py

@@ -17,19 +17,18 @@ _OPERATORS = {
 def _evaluate(node, names):
     if isinstance(node, ast.Num):
         return node.n
-    elif isinstance(node, ast.Name):
+    if isinstance(node, ast.Name):
         return names[node.id]
-    elif isinstance(node, ast.UnaryOp):
+    if isinstance(node, ast.UnaryOp):
         operand = _evaluate(node.operand, names=names)
         return _OPERATORS[type(node.op)](operand)
-    elif isinstance(node, ast.BinOp):
+    if isinstance(node, ast.BinOp):
         operand_left = _evaluate(node.left, names=names)
         operand_right = _evaluate(node.right, names=names)
         return _OPERATORS[type(node.op)](operand_left, operand_right)
-    else:
-        raise Exception(node)
+    raise Exception(node)
 
 
 def evaluate_expression(expr_str, names):
-    expr = ast.parse(expr_str, mode='eval')
+    expr = ast.parse(expr_str, mode="eval")
     return _evaluate(expr.body, names=names)

+ 2 - 0
mypy.ini

@@ -0,0 +1,2 @@
+[mypy]
+ignore_missing_imports = True

+ 12 - 10
setup.py

@@ -3,17 +3,19 @@
 import setuptools
 
 setuptools.setup(
-    name='acpi-backlight',
+    name="acpi-backlight",
     use_scm_version=True,
-    author='Fabian Peter Hammerle',
-    author_email='fabian@hammerle.me',
-    url='https://github.com/fphammerle/acpi-backlight',
-    packages=['acpi_backlight'],
-    entry_points={'console_scripts': [
-        'acpi-backlight-eval=acpi_backlight:main',
-    ]},
-    setup_requires=['setuptools_scm'],
-    tests_require=['pytest'],
+    author="Fabian Peter Hammerle",
+    author_email="fabian@hammerle.me",
+    url="https://github.com/fphammerle/acpi-backlight",
+    packages=["acpi_backlight"],
+    entry_points={
+        "console_scripts": [
+            "acpi-backlight-eval=acpi_backlight:main",
+        ]
+    },
+    setup_requires=["setuptools_scm"],
+    tests_require=["pytest"],
     classifiers=[
         "License :: OSI Approved :: MIT License",
         "Operating System :: POSIX :: Linux",

+ 91 - 0
test/test_backlight.py

@@ -0,0 +1,91 @@
+import unittest.mock
+
+import pytest
+
+from acpi_backlight import Backlight, backlight_eval
+
+# pylint: disable=protected-access
+# pylint: disable=redefined-outer-name; fixture
+
+
+def test_init_default():
+    backlight = Backlight()
+    assert backlight._acpi_dir_path == "/sys/class/backlight/intel_backlight"
+
+
+@pytest.mark.parametrize("name", ["intel_backlight", "other"])
+def test_init(name):
+    backlight = Backlight(name=name)
+    assert backlight._acpi_dir_path == "/sys/class/backlight/{}".format(name)
+
+
+@pytest.mark.parametrize(
+    ("max_brightness", "brightness_absolute_str", "expected_brightness_relative"),
+    (
+        (100, "0", 0),
+        (100, "100", 1),
+        (100, "40", 0.4),
+        (4096, "2048", 0.5),
+        (4096, "3584", 0.875),
+    ),
+)
+def test_brightness_relative_get(
+    tmp_path, max_brightness, brightness_absolute_str, expected_brightness_relative
+):
+    backlight = Backlight()
+    backlight._acpi_dir_path = str(tmp_path)
+    tmp_path.joinpath("brightness").write_text(brightness_absolute_str)
+    tmp_path.joinpath("max_brightness").write_text(str(max_brightness))
+    assert backlight.brightness_relative == pytest.approx(expected_brightness_relative)
+
+
+@pytest.mark.parametrize(
+    ("max_brightness", "brightness_relative", "expected_brightness_abs_str"),
+    (
+        (100, 0, "0"),
+        (100, 1, "100"),
+        (100, 0.4, "40"),
+        (4096, 0.5, "2048"),
+        (4096, 0.8, "3277"),
+        (100, 1.1, "100"),
+        (100, -0.1, "0"),
+    ),
+)
+def test_brightness_relative_set(
+    tmp_path, max_brightness, brightness_relative, expected_brightness_abs_str
+):
+    backlight = Backlight()
+    backlight._acpi_dir_path = str(tmp_path)
+    tmp_path.joinpath("max_brightness").write_text(str(max_brightness))
+    backlight.brightness_relative = brightness_relative
+    assert tmp_path.joinpath("brightness").read_text() == expected_brightness_abs_str
+
+
+@pytest.mark.parametrize(
+    ("current_brightness_relative", "expr_str", "expected_new_brightness_relative"),
+    (
+        (100, "1", 1),
+        (100, "0", 0),
+        (100, "0.42", 0.42),
+        (100, "1/4", 0.25),
+        (100, "b * 0.5", 50),
+        (100, "b / 2", 50),
+        (100, "b + 21", 121),
+        (100, "b - 21", 79),
+    ),
+)
+def test_backlight_eval(
+    current_brightness_relative, expr_str, expected_new_brightness_relative
+):
+    with unittest.mock.patch(
+        "acpi_backlight.Backlight.brightness_relative",
+        new_callable=unittest.mock.PropertyMock,
+    ) as property_mock:
+        property_mock.return_value = current_brightness_relative
+        backlight_eval(expr_str)
+    # args and kwargs properties were added in python3.8
+    assert all(not call[1] for call in property_mock.call_args_list)
+    setter_calls_args = [call[0] for call in property_mock.call_args_list if call[0]]
+    assert len(setter_calls_args) == 1, setter_calls_args
+    (new_brightness_relative,) = setter_calls_args[0]
+    assert new_brightness_relative == pytest.approx(expected_new_brightness_relative)

+ 29 - 23
tests/test_evaluate.py

@@ -5,33 +5,39 @@ import pytest
 from acpi_backlight.evaluate import evaluate_expression
 
 
-@pytest.mark.parametrize(('expr_str', 'names', 'expected'), [
-    ('0', {}, 0),
-    ('0.0', {}, 0),
-    ('1.0', {}, 1),
-    ('-1', {}, -1),
-    ('0.1 + 0.2', {}, 0.3),
-    ('0.3 - 0.2', {}, 0.1),
-    ('0.2 * 3', {}, 0.6),
-    ('0.6 / 3', {}, 0.2),
-    ('0.6 / 3 + 0.1', {}, 0.3),
-    ('(0.6 - 0.2) / 2', {}, 0.2),
-    ('b', {'b': 0.4}, 0.4),
-    ('-b', {'b': 0.3}, -0.3),
-    ('0.1 + b', {'b': 0.2}, 0.3),
-])
+@pytest.mark.parametrize(
+    ("expr_str", "names", "expected"),
+    [
+        ("0", {}, 0),
+        ("0.0", {}, 0),
+        ("1.0", {}, 1),
+        ("-1", {}, -1),
+        ("0.1 + 0.2", {}, 0.3),
+        ("0.3 - 0.2", {}, 0.1),
+        ("0.2 * 3", {}, 0.6),
+        ("0.6 / 3", {}, 0.2),
+        ("0.6 / 3 + 0.1", {}, 0.3),
+        ("(0.6 - 0.2) / 2", {}, 0.2),
+        ("b", {"b": 0.4}, 0.4),
+        ("-b", {"b": 0.3}, -0.3),
+        ("0.1 + b", {"b": 0.2}, 0.3),
+    ],
+)
 def test_evaluate_expression(expr_str, names, expected):
     assert expected == pytest.approx(evaluate_expression(expr_str, names))
 
 
-@pytest.mark.parametrize('expr_str', [
-    'read("/proc/cpuinfo")',
-    'os.exit(42)',
-    'os.system("echo evil")',
-    '0.__class__',
-    'None.__class__',
-    'eval("1")',
-])
+@pytest.mark.parametrize(
+    "expr_str",
+    [
+        'read("/proc/cpuinfo")',
+        "os.exit(42)",
+        'os.system("echo evil")',
+        "0.__class__",
+        "None.__class__",
+        'eval("1")',
+    ],
+)
 def test_evaluate_expression_fail(expr_str):
     with pytest.raises(Exception):
         evaluate_expression(expr_str, {})