Merge juju-lint:update-snap into juju-lint:master
- Git
- lp:juju-lint
- update-snap
- Merge into master
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Eric Chen | ||||
Approved revision: | 57cd0951d0e5e453d304291142a69dabff5c560e | ||||
Merged at revision: | 14ad2b7c55802329a6be651caa8a79773f0b3c2d | ||||
Proposed branch: | juju-lint:update-snap | ||||
Merge into: | juju-lint:master | ||||
Diff against target: |
808 lines (+138/-279) 13 files modified
Makefile (+4/-1) dev/null (+0/-199) jujulint/check_spaces.py (+12/-7) jujulint/cli.py (+7/-5) jujulint/cloud.py (+7/-5) jujulint/config.py (+2/-1) jujulint/lint.py (+41/-27) jujulint/logging.py (+2/-1) snap/snapcraft.yaml (+3/-3) tests/conftest.py (+3/-2) tests/requirements.txt (+3/-1) tests/test_jujulint.py (+28/-18) tox.ini (+26/-9) |
||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Eric Chen | Approve | ||
Martin Kalcok (community) | Approve | ||
Review via email: mp+424672@code.launchpad.net |
Commit message
update the snap
- added black to format the source code of the project and
applied it into the code.
- added isort to order imports and applied it into the code.
- changed python version from 3.6(deprecated) to 3.8.
- changed base to core20.
- added /snap/bin to $PATH
- removed Pipfile, Pipfile.lock and .python-version that is not
been used anymore.
Description of the change
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Martin Kalcok (martin-kalcok) wrote : | # |
I'm very much a fan of adding `black` and enforcing stricter rules. I have just few notes:
* I think that adding "black --check <targets>" to "lint" tox environment will help with CI integration
* Another useful tool is `isort` that manages format/ordering of python imports
If you want to check out example of other project that has these tools set up, you can check juju-verify [1].
Regarding the changes to snapcraft.yaml: Does juju-lint always depend on juju (snap)? If yes, maybe we should add it as `stage-snaps` [2]?
---
[1] https:/
[2] https:/
Garrett Neugent (thogarre) wrote (last edit ): | # |
This is not a blocker in anyway, but one other tool that is worth consideration is pre-commit. It can help enforce linting, unit tests, and other checks before engineers submit a new MR. Juju-backup-all has this implemented [0] if you think it's worth adding here. (Note that if you do consider adding this, you'll likely need to use v22.3.0 of black, not 21.8b0 as currently in juju-backup-all's config [1]).
[0] https:/
[1] https:/
Gabriel Cocenza (gabrielcocenza) wrote : | # |
Hi Martin. Thanks for your review. I've added isort and applied it in the project.
Regarding adding juju snap into `stage-snaps` I guess it's not necessary. I don't see a huge gain of having the binaries of juju snap into juju-lint. Maybe we should consider using pythonlibjuju instead of making subprocess calls to deal with juju?
Gabriel Cocenza (gabrielcocenza) wrote : | # |
Thanks for this suggestion Garret. I think it will be a very good thing for the project. I've added a bug[0] to add pre-commit in a future MR.
Robert Gildein (rgildein) wrote : | # |
I briefly look at this and how three questions/
- How do we use Pipenv here? Do you want to create a requirements.txt file? If
so, what do you say to add `pipenv lock -> requirements.txt` to tox.ini to
generate requirements from Pipfile? There is also simply way to check
requirements in Pipfile.lock and requirements.txt
`pipenv lock -r | diff requirements.txt -`
- If the above applies, we can use pipenv --dev to have testing requirements
there.
- You can add `--cov-fail-under <current-
function will need to have unittests.
Martin Kalcok (martin-kalcok) wrote : | # |
Thanks for including my suggestions. Just two more nitpicks
* you have misaligned indentations in the pytest command in `unit` tox environment
* shouldn't the isort line length config (in tox.ini) match the line length of `black` which is 88?
Gabriel Cocenza (gabrielcocenza) wrote : | # |
Thanks Martin. The problem in tox was mixed spaces and tabs. Regarding isort, you are right. I added the black profile [0] that will do the job.
Regarding the Pipfile(s), it looks like they are not been used (maybe just for those that use pipenv, so I removed then. The MR that added then it's this one [1].
[0] https:/
[1] https:/
Martin Kalcok (martin-kalcok) wrote : | # |
LGTM, thanks
Eric Chen (eric-chen) wrote : | # |
LGTM, please go ahead.
🤖 Canonical IS Merge Bot (canonical-is-mergebot) wrote : | # |
Change successfully merged at revision 14ad2b7c5580232
Preview Diff
1 | diff --git a/.python-version b/.python-version |
2 | deleted file mode 100644 |
3 | index a08ffae..0000000 |
4 | --- a/.python-version |
5 | +++ /dev/null |
6 | @@ -1 +0,0 @@ |
7 | -3.8.2 |
8 | diff --git a/Makefile b/Makefile |
9 | index 70a2762..85be332 100644 |
10 | --- a/Makefile |
11 | +++ b/Makefile |
12 | @@ -1,5 +1,5 @@ |
13 | lint: |
14 | - tox -e py3-lint |
15 | + tox -e lintverbose |
16 | |
17 | dch: |
18 | if ! which gbp > /dev/null; then sudo apt-get install -y git-buildpackage; fi |
19 | @@ -11,6 +11,9 @@ deb-src: |
20 | test: |
21 | tox -e unit |
22 | |
23 | +format-code: |
24 | + tox -e format-code |
25 | + |
26 | build: |
27 | snapcraft --use-lxd --debug |
28 | |
29 | diff --git a/Pipfile b/Pipfile |
30 | deleted file mode 100644 |
31 | index d2acf80..0000000 |
32 | --- a/Pipfile |
33 | +++ /dev/null |
34 | @@ -1,17 +0,0 @@ |
35 | -[[source]] |
36 | -url = "https://pypi.python.org/simple" |
37 | -verify_ssl = true |
38 | -name = "pypi" |
39 | - |
40 | -[packages] |
41 | -attrs = ">=18.1.0" |
42 | -colorlog = ">=4.1.0" |
43 | -confuse = ">=1.1.0" |
44 | -fabric2 = ">=2.5.0" |
45 | -setuptools = ">=46.1.3" |
46 | -PyYAML = ">=5.3.1" |
47 | - |
48 | -[dev-packages] |
49 | - |
50 | -[requires] |
51 | -python_version = "3.6" |
52 | diff --git a/Pipfile.lock b/Pipfile.lock |
53 | deleted file mode 100644 |
54 | index c43f7b6..0000000 |
55 | --- a/Pipfile.lock |
56 | +++ /dev/null |
57 | @@ -1,199 +0,0 @@ |
58 | -{ |
59 | - "_meta": { |
60 | - "hash": { |
61 | - "sha256": "e1534fcda4a023c68f70df4e53b34a9bafcd9ec48c4f8d93e20dda25b3c43cd4" |
62 | - }, |
63 | - "pipfile-spec": 6, |
64 | - "requires": { |
65 | - "python_version": "3.6" |
66 | - }, |
67 | - "sources": [ |
68 | - { |
69 | - "name": "pypi", |
70 | - "url": "https://pypi.python.org/simple", |
71 | - "verify_ssl": true |
72 | - } |
73 | - ] |
74 | - }, |
75 | - "default": { |
76 | - "attrs": { |
77 | - "hashes": [ |
78 | - "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", |
79 | - "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72" |
80 | - ], |
81 | - "index": "pypi", |
82 | - "version": "==19.3.0" |
83 | - }, |
84 | - "bcrypt": { |
85 | - "hashes": [ |
86 | - "sha256:0258f143f3de96b7c14f762c770f5fc56ccd72f8a1857a451c1cd9a655d9ac89", |
87 | - "sha256:0b0069c752ec14172c5f78208f1863d7ad6755a6fae6fe76ec2c80d13be41e42", |
88 | - "sha256:19a4b72a6ae5bb467fea018b825f0a7d917789bcfe893e53f15c92805d187294", |
89 | - "sha256:5432dd7b34107ae8ed6c10a71b4397f1c853bd39a4d6ffa7e35f40584cffd161", |
90 | - "sha256:6305557019906466fc42dbc53b46da004e72fd7a551c044a827e572c82191752", |
91 | - "sha256:69361315039878c0680be456640f8705d76cb4a3a3fe1e057e0f261b74be4b31", |
92 | - "sha256:6fe49a60b25b584e2f4ef175b29d3a83ba63b3a4df1b4c0605b826668d1b6be5", |
93 | - "sha256:74a015102e877d0ccd02cdeaa18b32aa7273746914a6c5d0456dd442cb65b99c", |
94 | - "sha256:763669a367869786bb4c8fcf731f4175775a5b43f070f50f46f0b59da45375d0", |
95 | - "sha256:8b10acde4e1919d6015e1df86d4c217d3b5b01bb7744c36113ea43d529e1c3de", |
96 | - "sha256:9fe92406c857409b70a38729dbdf6578caf9228de0aef5bc44f859ffe971a39e", |
97 | - "sha256:a190f2a5dbbdbff4b74e3103cef44344bc30e61255beb27310e2aec407766052", |
98 | - "sha256:a595c12c618119255c90deb4b046e1ca3bcfad64667c43d1166f2b04bc72db09", |
99 | - "sha256:c9457fa5c121e94a58d6505cadca8bed1c64444b83b3204928a866ca2e599105", |
100 | - "sha256:cb93f6b2ab0f6853550b74e051d297c27a638719753eb9ff66d1e4072be67133", |
101 | - "sha256:ce4e4f0deb51d38b1611a27f330426154f2980e66582dc5f438aad38b5f24fc1", |
102 | - "sha256:d7bdc26475679dd073ba0ed2766445bb5b20ca4793ca0db32b399dccc6bc84b7", |
103 | - "sha256:ff032765bb8716d9387fd5376d987a937254b0619eff0972779515b5c98820bc" |
104 | - ], |
105 | - "version": "==3.1.7" |
106 | - }, |
107 | - "cffi": { |
108 | - "hashes": [ |
109 | - "sha256:001bf3242a1bb04d985d63e138230802c6c8d4db3668fb545fb5005ddf5bb5ff", |
110 | - "sha256:00789914be39dffba161cfc5be31b55775de5ba2235fe49aa28c148236c4e06b", |
111 | - "sha256:028a579fc9aed3af38f4892bdcc7390508adabc30c6af4a6e4f611b0c680e6ac", |
112 | - "sha256:14491a910663bf9f13ddf2bc8f60562d6bc5315c1f09c704937ef17293fb85b0", |
113 | - "sha256:1cae98a7054b5c9391eb3249b86e0e99ab1e02bb0cc0575da191aedadbdf4384", |
114 | - "sha256:2089ed025da3919d2e75a4d963d008330c96751127dd6f73c8dc0c65041b4c26", |
115 | - "sha256:2d384f4a127a15ba701207f7639d94106693b6cd64173d6c8988e2c25f3ac2b6", |
116 | - "sha256:337d448e5a725bba2d8293c48d9353fc68d0e9e4088d62a9571def317797522b", |
117 | - "sha256:399aed636c7d3749bbed55bc907c3288cb43c65c4389964ad5ff849b6370603e", |
118 | - "sha256:3b911c2dbd4f423b4c4fcca138cadde747abdb20d196c4a48708b8a2d32b16dd", |
119 | - "sha256:3d311bcc4a41408cf5854f06ef2c5cab88f9fded37a3b95936c9879c1640d4c2", |
120 | - "sha256:62ae9af2d069ea2698bf536dcfe1e4eed9090211dbaafeeedf5cb6c41b352f66", |
121 | - "sha256:66e41db66b47d0d8672d8ed2708ba91b2f2524ece3dee48b5dfb36be8c2f21dc", |
122 | - "sha256:675686925a9fb403edba0114db74e741d8181683dcf216be697d208857e04ca8", |
123 | - "sha256:7e63cbcf2429a8dbfe48dcc2322d5f2220b77b2e17b7ba023d6166d84655da55", |
124 | - "sha256:8a6c688fefb4e1cd56feb6c511984a6c4f7ec7d2a1ff31a10254f3c817054ae4", |
125 | - "sha256:8c0ffc886aea5df6a1762d0019e9cb05f825d0eec1f520c51be9d198701daee5", |
126 | - "sha256:95cd16d3dee553f882540c1ffe331d085c9e629499ceadfbda4d4fde635f4b7d", |
127 | - "sha256:99f748a7e71ff382613b4e1acc0ac83bf7ad167fb3802e35e90d9763daba4d78", |
128 | - "sha256:b8c78301cefcf5fd914aad35d3c04c2b21ce8629b5e4f4e45ae6812e461910fa", |
129 | - "sha256:c420917b188a5582a56d8b93bdd8e0f6eca08c84ff623a4c16e809152cd35793", |
130 | - "sha256:c43866529f2f06fe0edc6246eb4faa34f03fe88b64a0a9a942561c8e22f4b71f", |
131 | - "sha256:cab50b8c2250b46fe738c77dbd25ce017d5e6fb35d3407606e7a4180656a5a6a", |
132 | - "sha256:cef128cb4d5e0b3493f058f10ce32365972c554572ff821e175dbc6f8ff6924f", |
133 | - "sha256:cf16e3cf6c0a5fdd9bc10c21687e19d29ad1fe863372b5543deaec1039581a30", |
134 | - "sha256:e56c744aa6ff427a607763346e4170629caf7e48ead6921745986db3692f987f", |
135 | - "sha256:e577934fc5f8779c554639376beeaa5657d54349096ef24abe8c74c5d9c117c3", |
136 | - "sha256:f2b0fa0c01d8a0c7483afd9f31d7ecf2d71760ca24499c8697aeb5ca37dc090c" |
137 | - ], |
138 | - "version": "==1.14.0" |
139 | - }, |
140 | - "colorlog": { |
141 | - "hashes": [ |
142 | - "sha256:30aaef5ab2a1873dec5da38fd6ba568fa761c9fa10b40241027fa3edea47f3d2", |
143 | - "sha256:732c191ebbe9a353ec160d043d02c64ddef9028de8caae4cfa8bd49b6afed53e" |
144 | - ], |
145 | - "index": "pypi", |
146 | - "version": "==4.1.0" |
147 | - }, |
148 | - "confuse": { |
149 | - "hashes": [ |
150 | - "sha256:adc1979ea6f4c0dd3d6fe06020c189843a649082ab8f6fb54db16f4ac5e5e1da" |
151 | - ], |
152 | - "index": "pypi", |
153 | - "version": "==1.1.0" |
154 | - }, |
155 | - "cryptography": { |
156 | - "hashes": [ |
157 | - "sha256:091d31c42f444c6f519485ed528d8b451d1a0c7bf30e8ca583a0cac44b8a0df6", |
158 | - "sha256:18452582a3c85b96014b45686af264563e3e5d99d226589f057ace56196ec78b", |
159 | - "sha256:1dfa985f62b137909496e7fc182dac687206d8d089dd03eaeb28ae16eec8e7d5", |
160 | - "sha256:1e4014639d3d73fbc5ceff206049c5a9a849cefd106a49fa7aaaa25cc0ce35cf", |
161 | - "sha256:22e91636a51170df0ae4dcbd250d318fd28c9f491c4e50b625a49964b24fe46e", |
162 | - "sha256:3b3eba865ea2754738616f87292b7f29448aec342a7c720956f8083d252bf28b", |
163 | - "sha256:651448cd2e3a6bc2bb76c3663785133c40d5e1a8c1a9c5429e4354201c6024ae", |
164 | - "sha256:726086c17f94747cedbee6efa77e99ae170caebeb1116353c6cf0ab67ea6829b", |
165 | - "sha256:844a76bc04472e5135b909da6aed84360f522ff5dfa47f93e3dd2a0b84a89fa0", |
166 | - "sha256:88c881dd5a147e08d1bdcf2315c04972381d026cdb803325c03fe2b4a8ed858b", |
167 | - "sha256:96c080ae7118c10fcbe6229ab43eb8b090fccd31a09ef55f83f690d1ef619a1d", |
168 | - "sha256:a0c30272fb4ddda5f5ffc1089d7405b7a71b0b0f51993cb4e5dbb4590b2fc229", |
169 | - "sha256:bb1f0281887d89617b4c68e8db9a2c42b9efebf2702a3c5bf70599421a8623e3", |
170 | - "sha256:c447cf087cf2dbddc1add6987bbe2f767ed5317adb2d08af940db517dd704365", |
171 | - "sha256:c4fd17d92e9d55b84707f4fd09992081ba872d1a0c610c109c18e062e06a2e55", |
172 | - "sha256:d0d5aeaedd29be304848f1c5059074a740fa9f6f26b84c5b63e8b29e73dfc270", |
173 | - "sha256:daf54a4b07d67ad437ff239c8a4080cfd1cc7213df57d33c97de7b4738048d5e", |
174 | - "sha256:e993468c859d084d5579e2ebee101de8f5a27ce8e2159959b6673b418fd8c785", |
175 | - "sha256:f118a95c7480f5be0df8afeb9a11bd199aa20afab7a96bcf20409b411a3a85f0" |
176 | - ], |
177 | - "version": "==2.9.2" |
178 | - }, |
179 | - "fabric2": { |
180 | - "hashes": [ |
181 | - "sha256:29edd7848420df589a49743394a0ae6874ccb6a9fe6413c0076d42cc290dcad6", |
182 | - "sha256:8838d9641fd4e95bfc2568aa16fc683a600de860ac52a1dc9675a4db3c6cef7c" |
183 | - ], |
184 | - "index": "pypi", |
185 | - "version": "==2.5.0" |
186 | - }, |
187 | - "invoke": { |
188 | - "hashes": [ |
189 | - "sha256:87b3ef9d72a1667e104f89b159eaf8a514dbf2f3576885b2bbdefe74c3fb2132", |
190 | - "sha256:93e12876d88130c8e0d7fd6618dd5387d6b36da55ad541481dfa5e001656f134", |
191 | - "sha256:de3f23bfe669e3db1085789fd859eb8ca8e0c5d9c20811e2407fa042e8a5e15d" |
192 | - ], |
193 | - "version": "==1.4.1" |
194 | - }, |
195 | - "paramiko": { |
196 | - "hashes": [ |
197 | - "sha256:920492895db8013f6cc0179293147f830b8c7b21fdfc839b6bad760c27459d9f", |
198 | - "sha256:9c980875fa4d2cb751604664e9a2d0f69096643f5be4db1b99599fe114a97b2f" |
199 | - ], |
200 | - "version": "==2.7.1" |
201 | - }, |
202 | - "pycparser": { |
203 | - "hashes": [ |
204 | - "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0", |
205 | - "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705" |
206 | - ], |
207 | - "version": "==2.20" |
208 | - }, |
209 | - "pynacl": { |
210 | - "hashes": [ |
211 | - "sha256:06cbb4d9b2c4bd3c8dc0d267416aaed79906e7b33f114ddbf0911969794b1cc4", |
212 | - "sha256:11335f09060af52c97137d4ac54285bcb7df0cef29014a1a4efe64ac065434c4", |
213 | - "sha256:2fe0fc5a2480361dcaf4e6e7cea00e078fcda07ba45f811b167e3f99e8cff574", |
214 | - "sha256:30f9b96db44e09b3304f9ea95079b1b7316b2b4f3744fe3aaecccd95d547063d", |
215 | - "sha256:511d269ee845037b95c9781aa702f90ccc36036f95d0f31373a6a79bd8242e25", |
216 | - "sha256:537a7ccbea22905a0ab36ea58577b39d1fa9b1884869d173b5cf111f006f689f", |
217 | - "sha256:54e9a2c849c742006516ad56a88f5c74bf2ce92c9f67435187c3c5953b346505", |
218 | - "sha256:757250ddb3bff1eecd7e41e65f7f833a8405fede0194319f87899690624f2122", |
219 | - "sha256:7757ae33dae81c300487591c68790dfb5145c7d03324000433d9a2c141f82af7", |
220 | - "sha256:7c6092102219f59ff29788860ccb021e80fffd953920c4a8653889c029b2d420", |
221 | - "sha256:8122ba5f2a2169ca5da936b2e5a511740ffb73979381b4229d9188f6dcb22f1f", |
222 | - "sha256:9c4a7ea4fb81536c1b1f5cc44d54a296f96ae78c1ebd2311bd0b60be45a48d96", |
223 | - "sha256:cd401ccbc2a249a47a3a1724c2918fcd04be1f7b54eb2a5a71ff915db0ac51c6", |
224 | - "sha256:d452a6746f0a7e11121e64625109bc4468fc3100452817001dbe018bb8b08514", |
225 | - "sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff", |
226 | - "sha256:f8851ab9041756003119368c1e6cd0b9c631f46d686b3904b18c0139f4419f80" |
227 | - ], |
228 | - "version": "==1.4.0" |
229 | - }, |
230 | - "pyyaml": { |
231 | - "hashes": [ |
232 | - "sha256:06a0d7ba600ce0b2d2fe2e78453a470b5a6e000a985dd4a4e54e436cc36b0e97", |
233 | - "sha256:240097ff019d7c70a4922b6869d8a86407758333f02203e0fc6ff79c5dcede76", |
234 | - "sha256:4f4b913ca1a7319b33cfb1369e91e50354d6f07a135f3b901aca02aa95940bd2", |
235 | - "sha256:69f00dca373f240f842b2931fb2c7e14ddbacd1397d57157a9b005a6a9942648", |
236 | - "sha256:73f099454b799e05e5ab51423c7bcf361c58d3206fa7b0d555426b1f4d9a3eaf", |
237 | - "sha256:74809a57b329d6cc0fdccee6318f44b9b8649961fa73144a98735b0aaf029f1f", |
238 | - "sha256:7739fc0fa8205b3ee8808aea45e968bc90082c10aef6ea95e855e10abf4a37b2", |
239 | - "sha256:95f71d2af0ff4227885f7a6605c37fd53d3a106fcab511b8860ecca9fcf400ee", |
240 | - "sha256:b8eac752c5e14d3eca0e6dd9199cd627518cb5ec06add0de9d32baeee6fe645d", |
241 | - "sha256:cc8955cfbfc7a115fa81d85284ee61147059a753344bc51098f3ccd69b0d7e0c", |
242 | - "sha256:d13155f591e6fcc1ec3b30685d50bf0711574e2c0dfffd7644babf8b5102ca1a" |
243 | - ], |
244 | - "index": "pypi", |
245 | - "version": "==5.3.1" |
246 | - }, |
247 | - "six": { |
248 | - "hashes": [ |
249 | - "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", |
250 | - "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" |
251 | - ], |
252 | - "version": "==1.15.0" |
253 | - } |
254 | - }, |
255 | - "develop": {} |
256 | -} |
257 | diff --git a/jujulint/check_spaces.py b/jujulint/check_spaces.py |
258 | index 47c0c63..17b8d8c 100644 |
259 | --- a/jujulint/check_spaces.py |
260 | +++ b/jujulint/check_spaces.py |
261 | @@ -20,7 +20,9 @@ class Relation: |
262 | While Juju does define separate provider and requirer roles, we'll ignore |
263 | those here. |
264 | """ |
265 | - return set([self.endpoint1, self.endpoint2]) == set([other.endpoint1, other.endpoint2]) |
266 | + return set([self.endpoint1, self.endpoint2]) == set( |
267 | + [other.endpoint1, other.endpoint2] |
268 | + ) |
269 | |
270 | @property |
271 | def endpoints(self): |
272 | @@ -45,7 +47,8 @@ class SpaceMismatch: |
273 | def __str__(self): |
274 | """Stringify the object.""" |
275 | return "SpaceMismatch({} (space {}) != {} (space {}))".format( |
276 | - self.endpoint1, self.space1, self.endpoint2, self.space2) |
277 | + self.endpoint1, self.space1, self.endpoint2, self.space2 |
278 | + ) |
279 | |
280 | @property |
281 | def relation(self): |
282 | @@ -54,8 +57,8 @@ class SpaceMismatch: |
283 | |
284 | def get_charm_relation(self, app_to_charm_map): |
285 | """Return a relation object, mapping applications to charms.""" |
286 | - app1, endpoint1 = self.endpoint1.split(':') |
287 | - app2, endpoint2 = self.endpoint2.split(':') |
288 | + app1, endpoint1 = self.endpoint1.split(":") |
289 | + app2, endpoint2 = self.endpoint2.split(":") |
290 | charm1 = app_to_charm_map[app1] |
291 | charm2 = app_to_charm_map[app2] |
292 | return Relation(":".join([charm1, endpoint1]), ":".join([charm2, endpoint2])) |
293 | @@ -92,7 +95,9 @@ def find_space_mismatches(parsed_yaml, debug=False): |
294 | space1 = get_relation_space(relation.endpoint1, app_spaces) |
295 | space2 = get_relation_space(relation.endpoint2, app_spaces) |
296 | if space1 != space2: |
297 | - mismatch = SpaceMismatch(relation.endpoint1, space1, relation.endpoint2, space2) |
298 | + mismatch = SpaceMismatch( |
299 | + relation.endpoint1, space1, relation.endpoint2, space2 |
300 | + ) |
301 | mismatches.append(mismatch) |
302 | |
303 | if debug: |
304 | @@ -112,7 +117,7 @@ def get_application_spaces(application_list, parsed_yaml): |
305 | app_spaces = {} |
306 | for app in application_list: |
307 | app_spaces[app] = {} |
308 | - bindings = parsed_yaml["applications"][app]['bindings'] |
309 | + bindings = parsed_yaml["applications"][app]["bindings"] |
310 | for name, value in bindings.items(): |
311 | app_spaces[app][name] = value |
312 | return app_spaces |
313 | @@ -129,7 +134,7 @@ def get_application_relations(parsed_yaml): |
314 | |
315 | def get_relation_space(endpoint, app_spaces): |
316 | """Get space for specified app and service.""" |
317 | - app, service = endpoint.split(':') |
318 | + app, service = endpoint.split(":") |
319 | if app in app_spaces: |
320 | if service not in app_spaces[app]: |
321 | return app_spaces[app][""] |
322 | diff --git a/jujulint/cli.py b/jujulint/cli.py |
323 | index af5190a..2d76832 100755 |
324 | --- a/jujulint/cli.py |
325 | +++ b/jujulint/cli.py |
326 | @@ -17,15 +17,17 @@ |
327 | # You should have received a copy of the GNU General Public License |
328 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
329 | """Main entrypoint for the juju-lint CLI.""" |
330 | -from jujulint.config import Config |
331 | -from jujulint.lint import Linter |
332 | -from jujulint.logging import Logger |
333 | -from jujulint.openstack import OpenStack |
334 | import logging |
335 | import os.path |
336 | +import sys |
337 | + |
338 | import pkg_resources |
339 | import yaml |
340 | -import sys |
341 | + |
342 | +from jujulint.config import Config |
343 | +from jujulint.lint import Linter |
344 | +from jujulint.logging import Logger |
345 | +from jujulint.openstack import OpenStack |
346 | |
347 | |
348 | class Cli: |
349 | diff --git a/jujulint/cloud.py b/jujulint/cloud.py |
350 | index b3cde24..3382e3b 100644 |
351 | --- a/jujulint/cloud.py |
352 | +++ b/jujulint/cloud.py |
353 | @@ -33,13 +33,15 @@ Todo: |
354 | * Add function to run command on a unit, via fabric and jump host if configured |
355 | |
356 | """ |
357 | -from fabric2 import Connection, Config |
358 | -from paramiko.ssh_exception import SSHException |
359 | -from jujulint.logging import Logger |
360 | -from jujulint.lint import Linter |
361 | -from subprocess import check_output |
362 | import socket |
363 | +from subprocess import check_output |
364 | + |
365 | import yaml |
366 | +from fabric2 import Config, Connection |
367 | +from paramiko.ssh_exception import SSHException |
368 | + |
369 | +from jujulint.lint import Linter |
370 | +from jujulint.logging import Logger |
371 | |
372 | |
373 | class Cloud: |
374 | diff --git a/jujulint/config.py b/jujulint/config.py |
375 | index 1173187..d87677a 100644 |
376 | --- a/jujulint/config.py |
377 | +++ b/jujulint/config.py |
378 | @@ -18,9 +18,10 @@ |
379 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
380 | """Config handling routines.""" |
381 | |
382 | -from confuse import Configuration |
383 | from argparse import ArgumentParser |
384 | |
385 | +from confuse import Configuration |
386 | + |
387 | |
388 | class Config(Configuration): |
389 | """Helper class for holding parsed config, extending confuse's BaseConfiguraion class.""" |
390 | diff --git a/jujulint/lint.py b/jujulint/lint.py |
391 | index 404e8b8..e908b7a 100755 |
392 | --- a/jujulint/lint.py |
393 | +++ b/jujulint/lint.py |
394 | @@ -19,24 +19,23 @@ |
395 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
396 | """Lint operations and rule processing engine.""" |
397 | import collections |
398 | -from datetime import datetime, timezone |
399 | import json |
400 | import logging |
401 | import os.path |
402 | import pprint |
403 | import re |
404 | import traceback |
405 | +from datetime import datetime, timezone |
406 | |
407 | -import yaml |
408 | - |
409 | -from attr import attrs, attrib |
410 | import attr |
411 | import dateutil.parser |
412 | +import yaml |
413 | +from attr import attrib, attrs |
414 | from dateutil import relativedelta |
415 | |
416 | import jujulint.util as utils |
417 | +from jujulint.check_spaces import Relation, find_space_mismatches |
418 | from jujulint.logging import Logger |
419 | -from jujulint.check_spaces import find_space_mismatches, Relation |
420 | |
421 | VALID_CONFIG_CHECKS = ("isset", "eq", "neq", "gte") |
422 | |
423 | @@ -195,7 +194,7 @@ class Linter: |
424 | if val[-1].lower() == val[-1]: |
425 | quotient = 1000 |
426 | |
427 | - conv = {"g": quotient ** 3, "m": quotient ** 2, "k": quotient} |
428 | + conv = {"g": quotient**3, "m": quotient**2, "k": quotient} |
429 | |
430 | return _int * conv[val[-1].lower()] |
431 | |
432 | @@ -684,21 +683,39 @@ class Linter: |
433 | enforce_endpoints = space_checks.get("enforce endpoints", []) |
434 | enforce_relations = [ |
435 | Relation(*relation) |
436 | - for relation in space_checks.get("enforce relations", [])] |
437 | + for relation in space_checks.get("enforce relations", []) |
438 | + ] |
439 | ignore_endpoints = space_checks.get("ignore endpoints", []) |
440 | ignore_relations = [ |
441 | - Relation(*relation) |
442 | - for relation in space_checks.get("ignore relations", [])] |
443 | + Relation(*relation) for relation in space_checks.get("ignore relations", []) |
444 | + ] |
445 | |
446 | mismatches = find_space_mismatches(parsed_yaml) |
447 | for mismatch in mismatches: |
448 | try: |
449 | - self._handle_space_mismatch(mismatch, enforce_endpoints, enforce_relations, ignore_endpoints, ignore_relations) |
450 | + self._handle_space_mismatch( |
451 | + mismatch, |
452 | + enforce_endpoints, |
453 | + enforce_relations, |
454 | + ignore_endpoints, |
455 | + ignore_relations, |
456 | + ) |
457 | except Exception: |
458 | # FOR NOW: super quick and dirty |
459 | - print('Exception caught during space check; please check space by hand. {}'.format(traceback.format_exc())) |
460 | + print( |
461 | + "Exception caught during space check; please check space by hand. {}".format( |
462 | + traceback.format_exc() |
463 | + ) |
464 | + ) |
465 | |
466 | - def _handle_space_mismatch(self, mismatch, enforce_endpoints, enforce_relations, ignore_endpoints, ignore_relations): |
467 | + def _handle_space_mismatch( |
468 | + self, |
469 | + mismatch, |
470 | + enforce_endpoints, |
471 | + enforce_relations, |
472 | + ignore_endpoints, |
473 | + ignore_relations, |
474 | + ): |
475 | # By default: treat mismatches as warnings. |
476 | # If we have a matching enforcement rule, treat as an error. |
477 | # If we have a matching ignore rule, do not warn. |
478 | @@ -722,12 +739,14 @@ class Linter: |
479 | |
480 | message = "Space binding mismatch: {}".format(mismatch) |
481 | if error: |
482 | - self.handle_error({ |
483 | - "id": "space-binding-mismatch", |
484 | - "tags": ["mismatch", "space", "binding"], |
485 | - "description": "Unhandled space binding mismatch", |
486 | - "message": message, |
487 | - }) |
488 | + self.handle_error( |
489 | + { |
490 | + "id": "space-binding-mismatch", |
491 | + "tags": ["mismatch", "space", "binding"], |
492 | + "description": "Unhandled space binding mismatch", |
493 | + "message": message, |
494 | + } |
495 | + ) |
496 | elif warning: |
497 | # DEFAULT: not a critical error, so just warn |
498 | self._log_with_header(message, level=logging.WARN) |
499 | @@ -1041,7 +1060,7 @@ class Linter: |
500 | offer_overlay = True |
501 | if parsed_yaml is None or not offer_overlay: |
502 | parsed_yaml = doc |
503 | - return(parsed_yaml) |
504 | + return parsed_yaml |
505 | |
506 | def lint_yaml_string(self, yaml_string): |
507 | """Lint provided YAML string.""" |
508 | @@ -1086,15 +1105,10 @@ class Linter: |
509 | |
510 | if "relations" in parsed_yaml: |
511 | # "bindings" *should* be in exported bundles, *unless* no custom bindings exist, |
512 | - # in which case "juju export-bundle" omits them. |
513 | + # in which case "juju export-bundle" omits them. See LP#1949883. |
514 | if "bindings" in list(parsed_yaml[applications].values())[0]: |
515 | - #try: |
516 | - self.check_spaces(parsed_yaml) |
517 | - #except Exception as e: |
518 | - # self._log_with_header( |
519 | - # "Encountered error while checking spaces: {}".format(e), |
520 | - # level=logging.WARN |
521 | - # ) |
522 | + self.check_spaces(parsed_yaml) |
523 | + |
524 | else: |
525 | self._log_with_header( |
526 | "Relations detected but custom bindings not found; " |
527 | diff --git a/jujulint/logging.py b/jujulint/logging.py |
528 | index e3c42f8..1d1586d 100644 |
529 | --- a/jujulint/logging.py |
530 | +++ b/jujulint/logging.py |
531 | @@ -17,10 +17,11 @@ |
532 | # You should have received a copy of the GNU General Public License |
533 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
534 | """Logging helper functions.""" |
535 | -import colorlog |
536 | import logging |
537 | import sys |
538 | |
539 | +import colorlog |
540 | + |
541 | |
542 | class Logger: |
543 | """Helper class for logging.""" |
544 | diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml |
545 | index 9f84064..555e9af 100644 |
546 | --- a/snap/snapcraft.yaml |
547 | +++ b/snap/snapcraft.yaml |
548 | @@ -1,6 +1,6 @@ |
549 | --- |
550 | name: juju-lint |
551 | -base: core18 |
552 | +base: core20 |
553 | summary: Linter for Juju models to compare deployments with configurable policy |
554 | adopt-info: juju-lint |
555 | description: | |
556 | @@ -13,8 +13,8 @@ apps: |
557 | juju-lint: |
558 | command: bin/juju-lint |
559 | environment: |
560 | - PATH: "/snap/juju-lint/current/bin:/snap/juju-lint/current/usr/bin:/bin:/usr/bin:" |
561 | - PYTHONPATH: $SNAP/usr/lib/python3.6/site-packages:$SNAP/usr/lib/python3.6/dist-packages:$PYTHONPATH |
562 | + PATH: "/snap/juju-lint/current/bin:/snap/juju-lint/current/usr/bin:/bin:/usr/bin:/snap/bin" |
563 | + PYTHONPATH: $SNAP/usr/lib/python3.8/site-packages:$SNAP/usr/lib/python3.8/dist-packages:$PYTHONPATH |
564 | parts: |
565 | juju-lint: |
566 | plugin: python |
567 | diff --git a/tests/conftest.py b/tests/conftest.py |
568 | index fc7093e..55ac1ac 100644 |
569 | --- a/tests/conftest.py |
570 | +++ b/tests/conftest.py |
571 | @@ -8,11 +8,12 @@ |
572 | |
573 | """Test fixtures for juju-lint tool.""" |
574 | |
575 | -import mock |
576 | import os |
577 | -import pytest |
578 | import sys |
579 | |
580 | +import mock |
581 | +import pytest |
582 | + |
583 | # bring in top level library to path |
584 | test_path = os.path.dirname(os.path.abspath(__file__)) |
585 | sys.path.insert(0, test_path + "/../") |
586 | diff --git a/tests/requirements.txt b/tests/requirements.txt |
587 | index b40fd2a..2575eb3 100644 |
588 | --- a/tests/requirements.txt |
589 | +++ b/tests/requirements.txt |
590 | @@ -1,8 +1,10 @@ |
591 | # Module requirements |
592 | +black |
593 | flake8 |
594 | flake8-colors |
595 | flake8-docstrings |
596 | flake8-html |
597 | +isort |
598 | mock |
599 | pep8-naming |
600 | pycodestyle |
601 | @@ -10,4 +12,4 @@ pyflakes |
602 | pytest |
603 | pytest-cov |
604 | pytest-html |
605 | -pytest-mock |
606 | \ No newline at end of file |
607 | +pytest-mock |
608 | diff --git a/tests/test_jujulint.py b/tests/test_jujulint.py |
609 | index c4c51f3..cff1d6b 100644 |
610 | --- a/tests/test_jujulint.py |
611 | +++ b/tests/test_jujulint.py |
612 | @@ -1,8 +1,8 @@ |
613 | #!/usr/bin/python3 |
614 | """Tests for jujulint.""" |
615 | +import logging |
616 | from datetime import datetime, timezone |
617 | from unittest import mock |
618 | -import logging |
619 | |
620 | import pytest |
621 | |
622 | @@ -106,7 +106,7 @@ class TestLinter: |
623 | "test-app-4": {"charm": "local:SERIES/TEST-CHARM12"}, |
624 | "test-app-5": {"charm": "local:TEST-CHARM12"}, |
625 | "test-app-6": {"charm": "cs:~TEST-CHARMERS/TEST-CHARM12-123"}, |
626 | - "test-app-7": {"charm": "ch:amd64/bionic/TEST-CHARM12-123"} |
627 | + "test-app-7": {"charm": "ch:amd64/bionic/TEST-CHARM12-123"}, |
628 | } |
629 | linter.map_charms(applications) |
630 | for charm in linter.model.charms: |
631 | @@ -524,9 +524,7 @@ applications: |
632 | |
633 | def test_config_eq_no_suffix_check_all(self, linter, juju_status): |
634 | """Test the config condition 'eq'. when no suffix all should be checked.""" |
635 | - linter.lint_rules["config"] = { |
636 | - "ubuntu": {"fake-opt": {"eq": False}} |
637 | - } |
638 | + linter.lint_rules["config"] = {"ubuntu": {"fake-opt": {"eq": False}}} |
639 | juju_status["applications"]["ubuntu"]["options"] = {"fake-opt": True} |
640 | juju_status["applications"]["ubuntu-host"] = juju_status["applications"].pop( |
641 | "ubuntu" |
642 | @@ -720,21 +718,21 @@ applications: |
643 | } |
644 | |
645 | def test_check_spaces_detect_mismatches(self, linter, mocker): |
646 | - mock_log: mock.MagicMock = mocker.patch('jujulint.lint.Linter._log_with_header') |
647 | + mock_log: mock.MagicMock = mocker.patch("jujulint.lint.Linter._log_with_header") |
648 | linter.model.app_to_charm = self.check_spaces_example_app_charm_map |
649 | |
650 | # Run the space check. |
651 | # Based on the above bundle, we should have exactly one mismatch. |
652 | linter.check_spaces(self.check_spaces_example_bundle) |
653 | - |
654 | + |
655 | # By default the mismatch should only trigger a warning, not an error. |
656 | errors = linter.output_collector["errors"] |
657 | assert len(errors) == 0 |
658 | assert mock_log.call_count == 1 |
659 | - assert mock_log.mock_calls[0].kwargs['level'] == logging.WARN |
660 | + assert mock_log.mock_calls[0].kwargs["level"] == logging.WARN |
661 | assert mock_log.mock_calls[0].args[0] == ( |
662 | - 'Space binding mismatch: SpaceMismatch(telegraf-app:prometheus-client ' |
663 | - '(space external-space) != prometheus-app:target (space internal-space))' |
664 | + "Space binding mismatch: SpaceMismatch(prometheus-app:target " |
665 | + "(space internal-space) != telegraf-app:prometheus-client (space external-space))" |
666 | ) |
667 | |
668 | def test_check_spaces_enforce_endpoints(self, linter): |
669 | @@ -749,7 +747,9 @@ applications: |
670 | |
671 | # Enforce the opposite end of the relation. |
672 | # This should also generate an error. |
673 | - linter.lint_rules["space checks"] = {"enforce endpoints": ["telegraf:prometheus-client"]} |
674 | + linter.lint_rules["space checks"] = { |
675 | + "enforce endpoints": ["telegraf:prometheus-client"] |
676 | + } |
677 | linter.check_spaces(self.check_spaces_example_bundle) |
678 | errors = linter.output_collector["errors"] |
679 | assert len(errors) == 2 |
680 | @@ -759,20 +759,24 @@ applications: |
681 | |
682 | # Run the space check with prometheus:target endpoint enforced. |
683 | # This should generate an error. |
684 | - linter.lint_rules["space checks"] = {"enforce relations": [["prometheus:target", "telegraf:prometheus-client"]]} |
685 | + linter.lint_rules["space checks"] = { |
686 | + "enforce relations": [["prometheus:target", "telegraf:prometheus-client"]] |
687 | + } |
688 | linter.check_spaces(self.check_spaces_example_bundle) |
689 | errors = linter.output_collector["errors"] |
690 | assert len(errors) == 1 |
691 | |
692 | # Reverse the relation's definition order. |
693 | # This should work the same way and also generate an error. |
694 | - linter.lint_rules["space checks"] = {"enforce relations": [["telegraf:prometheus-client", "prometheus:target"]]} |
695 | + linter.lint_rules["space checks"] = { |
696 | + "enforce relations": [["telegraf:prometheus-client", "prometheus:target"]] |
697 | + } |
698 | linter.check_spaces(self.check_spaces_example_bundle) |
699 | errors = linter.output_collector["errors"] |
700 | assert len(errors) == 2 |
701 | |
702 | def test_check_spaces_ignore_endpoints(self, linter, mocker): |
703 | - mock_log: mock.MagicMock = mocker.patch('jujulint.lint.Linter._log_with_header') |
704 | + mock_log: mock.MagicMock = mocker.patch("jujulint.lint.Linter._log_with_header") |
705 | linter.model.app_to_charm = self.check_spaces_example_app_charm_map |
706 | |
707 | # Run the space check with prometheus:target endpoint ignored. |
708 | @@ -785,19 +789,23 @@ applications: |
709 | |
710 | # Enforce the opposite end of the relation. |
711 | # This should also generate an error. |
712 | - linter.lint_rules["space checks"] = {"ignore endpoints": ["telegraf:prometheus-client"]} |
713 | + linter.lint_rules["space checks"] = { |
714 | + "ignore endpoints": ["telegraf:prometheus-client"] |
715 | + } |
716 | linter.check_spaces(self.check_spaces_example_bundle) |
717 | errors = linter.output_collector["errors"] |
718 | assert len(errors) == 0 |
719 | assert mock_log.call_count == 0 |
720 | |
721 | def test_check_spaces_ignore_relations(self, linter, mocker): |
722 | - mock_log: mock.MagicMock = mocker.patch('jujulint.lint.Linter._log_with_header') |
723 | + mock_log: mock.MagicMock = mocker.patch("jujulint.lint.Linter._log_with_header") |
724 | linter.model.app_to_charm = self.check_spaces_example_app_charm_map |
725 | |
726 | # Run the space check with prometheus:target endpoint ignored. |
727 | # This should neither generate an error nor a warning. |
728 | - linter.lint_rules["space checks"] = {"ignore relations": [["prometheus:target", "telegraf:prometheus-client"]]} |
729 | + linter.lint_rules["space checks"] = { |
730 | + "ignore relations": [["prometheus:target", "telegraf:prometheus-client"]] |
731 | + } |
732 | linter.check_spaces(self.check_spaces_example_bundle) |
733 | errors = linter.output_collector["errors"] |
734 | assert len(errors) == 0 |
735 | @@ -805,7 +813,9 @@ applications: |
736 | |
737 | # Reverse the relation's definition order. |
738 | # This should work the same way and also not generate errors/warnings. |
739 | - linter.lint_rules["space checks"] = {"ignore relations": [["telegraf:prometheus-client", "prometheus:target"]]} |
740 | + linter.lint_rules["space checks"] = { |
741 | + "ignore relations": [["telegraf:prometheus-client", "prometheus:target"]] |
742 | + } |
743 | linter.check_spaces(self.check_spaces_example_bundle) |
744 | errors = linter.output_collector["errors"] |
745 | assert len(errors) == 0 |
746 | diff --git a/tox.ini b/tox.ini |
747 | index 3e9dbdf..843a442 100644 |
748 | --- a/tox.ini |
749 | +++ b/tox.ini |
750 | @@ -15,7 +15,7 @@ envlist = lintverbose, unit |
751 | skip_missing_interpreters = True |
752 | |
753 | [testenv] |
754 | -basepython = python3 |
755 | +basepython = python3.8 |
756 | deps = |
757 | -r{toxinidir}/tests/requirements.txt |
758 | -r{toxinidir}/requirements.txt |
759 | @@ -24,20 +24,34 @@ commands = |
760 | test: python3 -m unittest discover tests |
761 | |
762 | [testenv:unit] |
763 | -commands = pytest -v \ |
764 | - --cov=jujulint \ |
765 | - --cov-report=term \ |
766 | - --cov-report=annotate:tests/report/coverage-annotated \ |
767 | - --cov-report=html:tests/report/coverage-html \ |
768 | - --html=tests/report/index.html \ |
769 | - --junitxml=tests/report/junit.xml |
770 | +commands = |
771 | + pytest -v \ |
772 | + --cov=jujulint \ |
773 | + --new-first \ |
774 | + --last-failed \ |
775 | + --last-failed-no-failures all \ |
776 | + --cov-report=term-missing \ |
777 | + --cov-report=annotate:tests/report/coverage-annotated \ |
778 | + --cov-report=html:tests/report/coverage-html \ |
779 | + --html=tests/report/index.html \ |
780 | + --junitxml=tests/report/junit.xml |
781 | setenv = PYTHONPATH={toxinidir}/lib |
782 | |
783 | [testenv:lint] |
784 | commands = flake8 jujulint --format=html --htmldir=tests/report/lint/ --tee |
785 | |
786 | [testenv:lintverbose] |
787 | -commands = flake8 jujulint |
788 | +commands = |
789 | + flake8 {toxinidir}/jujulint {toxinidir}/tests |
790 | + black --check {toxinidir}/jujulint {toxinidir}/tests |
791 | + isort --check {toxinidir}/jujulint {toxinidir}/tests |
792 | + |
793 | + |
794 | +[testenv:format-code] |
795 | +commands = |
796 | + black {toxinidir}/jujulint {toxinidir}/tests |
797 | + isort {toxinidir}/jujulint {toxinidir}/tests |
798 | + |
799 | |
800 | [testenv:lintjunit] |
801 | commands = flake8 jujulint --format junit-xml --output-file=report/lint/junit.xml |
802 | @@ -45,3 +59,6 @@ commands = flake8 jujulint --format junit-xml --output-file=report/lint/junit.xm |
803 | [pytest] |
804 | filterwarnings = |
805 | ignore::DeprecationWarning |
806 | + |
807 | +[isort] |
808 | +profile = black |
This merge proposal is being monitored by mergebot. Change the status to Approved to merge.