mirror of
https://github.com/dragonpilot/dragonpilot.git
synced 2026-06-12 06:54:12 +08:00
Compare commits
1091 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
204c68d027 | ||
|
|
a9a878548e | ||
|
|
1f8dffc444 | ||
|
|
ce44aa7696 | ||
|
|
bdea69d89b | ||
|
|
abd3c6b5dc | ||
|
|
f700370a8e | ||
|
|
f462f640f8 | ||
|
|
75618d6ce7 | ||
|
|
c25b3def82 | ||
|
|
1c75d7fb14 | ||
|
|
faf9a36067 | ||
|
|
50eae23440 | ||
|
|
14fc0424b8 | ||
|
|
7998cf08d3 | ||
|
|
0c0ba60ce4 | ||
|
|
926e5f397f | ||
|
|
1d328da675 | ||
|
|
39ee5ba5ad | ||
|
|
7983bbd3bd | ||
|
|
dc15b9cae9 | ||
|
|
7750dc7784 | ||
|
|
ad40d8f66d | ||
|
|
4f0fb2ce5d | ||
|
|
7500d48861 | ||
|
|
4ed8a032d4 | ||
|
|
a49dc4be02 | ||
|
|
3b2e7c208a | ||
|
|
c8ccddcb8d | ||
|
|
eaff7b4bb4 | ||
|
|
1ceb7ddf0e | ||
|
|
3c0be0b4cd | ||
|
|
5ed475b25e | ||
|
|
64dbc96480 | ||
|
|
58ca42e7ef | ||
|
|
5ede6a346a | ||
|
|
5cacbbab13 | ||
|
|
043e83a160 | ||
|
|
67c4121f62 | ||
|
|
a08e586783 | ||
|
|
22023ebd58 | ||
|
|
f85f3b36bc | ||
|
|
047924cb35 | ||
|
|
5af4d3aeed | ||
|
|
06f8fd53ec | ||
|
|
d9877e694c | ||
|
|
d603d5c9b8 | ||
|
|
8ad2640c11 | ||
|
|
2b721f7fc8 | ||
|
|
2a29774272 | ||
|
|
4f41bfcd9e | ||
|
|
5219d3c023 | ||
|
|
7c0d01a96c | ||
|
|
ee752d5669 | ||
|
|
88af74a3da | ||
|
|
2393c2264c | ||
|
|
44bab52730 | ||
|
|
4ad87130f9 | ||
|
|
c270f64a07 | ||
|
|
f17f264c1f | ||
|
|
8ca719e75a | ||
|
|
e36a14f1de | ||
|
|
6e8ff99781 | ||
|
|
9e26bd8a89 | ||
|
|
0df2160823 | ||
|
|
1ad2ce8b2a | ||
|
|
61663c77ff | ||
|
|
e5431eab5d | ||
|
|
09e9f25c29 | ||
|
|
344dfa9bca | ||
|
|
d3b7d741af | ||
|
|
ce54902f69 | ||
|
|
4fd5034aff | ||
|
|
4b962fef63 | ||
|
|
c61ee95eec | ||
|
|
b5fb96455d | ||
|
|
e8c31261c8 | ||
|
|
608c8e46fd | ||
|
|
0f8cf424ce | ||
|
|
9e20d79455 | ||
|
|
da1161d7d8 | ||
|
|
62d14b8122 | ||
|
|
1608739cf7 | ||
|
|
3db7389eb8 | ||
|
|
7c8f42c07e | ||
|
|
84fc30a84b | ||
|
|
fbbf03bb73 | ||
|
|
f096912cb5 | ||
|
|
270702f54c | ||
|
|
601d0b8f1e | ||
|
|
5416f2008e | ||
|
|
4d8444a4aa | ||
|
|
4730da4911 | ||
|
|
24ed87e568 | ||
|
|
3c658f93c0 | ||
|
|
f82539ddd4 | ||
|
|
cdfab1efeb | ||
|
|
d8501656f0 | ||
|
|
5eea31af91 | ||
|
|
3caa71ef1b | ||
|
|
ef33ef60a8 | ||
|
|
d9ac0507a3 | ||
|
|
3cd0270878 | ||
|
|
1c03ea3b39 | ||
|
|
7a8f3e1df3 | ||
|
|
2ea17f00cf | ||
|
|
3adf752f1a | ||
|
|
8803683a0c | ||
|
|
c37dd3ec37 | ||
|
|
30f707cf7a | ||
|
|
77aaead924 | ||
|
|
8e329e1e3a | ||
|
|
7ad5f4292e | ||
|
|
a7bfa413ae | ||
|
|
b773d54460 | ||
|
|
7f3f07e04e | ||
|
|
a1abe13da0 | ||
|
|
e55fd7ae4b | ||
|
|
d855a70690 | ||
|
|
400889ce65 | ||
|
|
2b706c4335 | ||
|
|
62cbbb2845 | ||
|
|
fa5e8de64d | ||
|
|
6b103dac74 | ||
|
|
07b8c4b0a0 | ||
|
|
0f61fe3a44 | ||
|
|
3efa000221 | ||
|
|
83c6eca4a1 | ||
|
|
d130c6c9a2 | ||
|
|
9798b12384 | ||
|
|
b67715b480 | ||
|
|
eec9bb71c4 | ||
|
|
4f4a90117b | ||
|
|
703f51ed56 | ||
|
|
38faf7f8a4 | ||
|
|
a78baac2e3 | ||
|
|
f66527d0b1 | ||
|
|
96bdc0a043 | ||
|
|
d895bd6813 | ||
|
|
55fb019272 | ||
|
|
a8da7e753c | ||
|
|
1305ea2483 | ||
|
|
050994f673 | ||
|
|
79bd9d42eb | ||
|
|
fc3a9adf29 | ||
|
|
7d256852c3 | ||
|
|
6a95cb2b06 | ||
|
|
74a2d08430 | ||
|
|
da9fddeaae | ||
|
|
860fe53c35 | ||
|
|
a5fe383928 | ||
|
|
6c87ac182c | ||
|
|
f7c925871a | ||
|
|
8f77246fcb | ||
|
|
ee3aedaaea | ||
|
|
73cc0f1283 | ||
|
|
935191e724 | ||
|
|
bfef97c7b8 | ||
|
|
3e4f64cc78 | ||
|
|
59440ba5b7 | ||
|
|
a57a05b99d | ||
|
|
a944f7d9ed | ||
|
|
c9d8833dfe | ||
|
|
1d2ea64938 | ||
|
|
73d6d1537a | ||
|
|
09162309d1 | ||
|
|
d927d61f85 | ||
|
|
fc7bb4ea1f | ||
|
|
185887db37 | ||
|
|
863d0e1c2c | ||
|
|
2c4d7a2cd4 | ||
|
|
e22b687197 | ||
|
|
ea472e00c3 | ||
|
|
9f33903798 | ||
|
|
8cb4ff08c7 | ||
|
|
9c220ebf44 | ||
|
|
4197340812 | ||
|
|
1880e7ebdc | ||
|
|
b9a32d6f61 | ||
|
|
ca26fe3087 | ||
|
|
d189446b68 | ||
|
|
bb273acc28 | ||
|
|
13280acb12 | ||
|
|
8d5ef40e8b | ||
|
|
246e687ef9 | ||
|
|
ced9202c46 | ||
|
|
11555e198e | ||
|
|
a5ebac6ede | ||
|
|
d5aba9035b | ||
|
|
aa09e16a60 | ||
|
|
3df522b4d1 | ||
|
|
987ed000b4 | ||
|
|
eeaa355c77 | ||
|
|
a6273f1a36 | ||
|
|
bbc3507854 | ||
|
|
b797a10b6e | ||
|
|
068a481769 | ||
|
|
9520c20c6b | ||
|
|
508dc5742c | ||
|
|
fe2a3caf51 | ||
|
|
364aa99ccf | ||
|
|
08bb061647 | ||
|
|
d18f5a0b17 | ||
|
|
8452cb5d58 | ||
|
|
0a398a474e | ||
|
|
96b8b66a5a | ||
|
|
1d75585593 | ||
|
|
2fa7df9c22 | ||
|
|
e932e5d0dc | ||
|
|
205a1f8121 | ||
|
|
8b68e26b36 | ||
|
|
d49bd6ac02 | ||
|
|
ebea7ebd2c | ||
|
|
e7c53b1a4e | ||
|
|
7abf659b90 | ||
|
|
11824f2756 | ||
|
|
e815923bb7 | ||
|
|
88442f0819 | ||
|
|
1e1c3b62f5 | ||
|
|
97c5965c65 | ||
|
|
98444c7783 | ||
|
|
b8b2ca7759 | ||
|
|
d96f8b17b4 | ||
|
|
beb11b88a1 | ||
|
|
b84cef9d95 | ||
|
|
762115f7f8 | ||
|
|
db820d39d1 | ||
|
|
6b79d5bc07 | ||
|
|
c62aa79af7 | ||
|
|
089165d1b9 | ||
|
|
ec73591c7a | ||
|
|
192fc94fe5 | ||
|
|
98f6903eda | ||
|
|
4d95c008c5 | ||
|
|
0d8dbd73a6 | ||
|
|
a5518a2330 | ||
|
|
bbdcb46dc1 | ||
|
|
d06bd5953b | ||
|
|
df694ca719 | ||
|
|
a64d4f7cca | ||
|
|
aee19a87eb | ||
|
|
57b54d6a8a | ||
|
|
d2b3ed0ec7 | ||
|
|
797644ded1 | ||
|
|
876256a268 | ||
|
|
b358137f1d | ||
|
|
cbf2c4e08a | ||
|
|
df46400d6d | ||
|
|
67fde00df2 | ||
|
|
2385b2c981 | ||
|
|
3986b37de0 | ||
|
|
5ffacb58ae | ||
|
|
0c1f24d48b | ||
|
|
420141f46c | ||
|
|
0bc81f9ace | ||
|
|
93e3c9d725 | ||
|
|
1bbe8389db | ||
|
|
0b0968dc38 | ||
|
|
79d38db009 | ||
|
|
85a70c096a | ||
|
|
d13c6cc6b3 | ||
|
|
d9f9d439b4 | ||
|
|
983ab3001c | ||
|
|
f1329f043e | ||
|
|
e9df97064d | ||
|
|
25a76581e6 | ||
|
|
deb7e30b6f | ||
|
|
720ceff07e | ||
|
|
18064840c4 | ||
|
|
f9a9b1ddf9 | ||
|
|
03a00ccf45 | ||
|
|
734b0b295a | ||
|
|
972e0a7de1 | ||
|
|
1003213bd1 | ||
|
|
0573aa7cea | ||
|
|
5992179d68 | ||
|
|
a139677386 | ||
|
|
db45c6c91b | ||
|
|
e081975298 | ||
|
|
dd0b95689d | ||
|
|
e3d3c20ebb | ||
|
|
0df46ccd55 | ||
|
|
d66a01fc98 | ||
|
|
afc7f88566 | ||
|
|
25d90b69dc | ||
|
|
c1a78bd98b | ||
|
|
f23adbffed | ||
|
|
caec02ac5e | ||
|
|
7ca22104d8 | ||
|
|
79e031b0f5 | ||
|
|
4f98316c45 | ||
|
|
c03ed0e02c | ||
|
|
9e3ff30c0c | ||
|
|
0e6c5775e4 | ||
|
|
b35cd304b0 | ||
|
|
70b2222ede | ||
|
|
96330c25ce | ||
|
|
9fa2a47525 | ||
|
|
106f43287e | ||
|
|
6009c36423 | ||
|
|
8621334d76 | ||
|
|
d4651fcc79 | ||
|
|
1b75ac55ac | ||
|
|
891d417f7c | ||
|
|
57411524ff | ||
|
|
e509158e18 | ||
|
|
b2b06e8a48 | ||
|
|
cb5883d57b | ||
|
|
f6868275d2 | ||
|
|
05479c4b7d | ||
|
|
06f47c1352 | ||
|
|
5c8114ef73 | ||
|
|
3bb4af65ae | ||
|
|
d022449502 | ||
|
|
46f4e0ac0a | ||
|
|
1fdf2d59b7 | ||
|
|
a1e0ba73c9 | ||
|
|
8f4281a10f | ||
|
|
b6e97ad135 | ||
|
|
f07e24cba4 | ||
|
|
a3e914ec7a | ||
|
|
1d498095aa | ||
|
|
249c6512bb | ||
|
|
c094c53b30 | ||
|
|
bd2c65ea6d | ||
|
|
81836bba3f | ||
|
|
99eec0aa47 | ||
|
|
47a0a48f0c | ||
|
|
6b0701c5f9 | ||
|
|
197143d3b7 | ||
|
|
816bc9d4ca | ||
|
|
16992bfb15 | ||
|
|
862c5507a1 | ||
|
|
c209d38d92 | ||
|
|
d900c8e9b2 | ||
|
|
52bfd679ad | ||
|
|
d31713a4a8 | ||
|
|
7b8a2043b9 | ||
|
|
c58f2a817c | ||
|
|
c4765f9aef | ||
|
|
255505e532 | ||
|
|
675801948e | ||
|
|
25634cc2a0 | ||
|
|
1e0f989d3f | ||
|
|
1c43aefada | ||
|
|
afbe7830a3 | ||
|
|
02f04dd953 | ||
|
|
2156e48aa9 | ||
|
|
dac25e5a14 | ||
|
|
cb0ee932c4 | ||
|
|
13e3783327 | ||
|
|
9955b3c806 | ||
|
|
80eb52d1dd | ||
|
|
a1ec1c089e | ||
|
|
96442bc9e1 | ||
|
|
d56f690432 | ||
|
|
b6a6fea700 | ||
|
|
88a47b0a96 | ||
|
|
a6088fd3e9 | ||
|
|
a22b600496 | ||
|
|
06a4ca837e | ||
|
|
a0e5e385d7 | ||
|
|
51069dc41e | ||
|
|
f72db32d6e | ||
|
|
97bfc183ef | ||
|
|
5d1e7ddbc2 | ||
|
|
958f69608c | ||
|
|
b83d573701 | ||
|
|
e67b0094b0 | ||
|
|
11f1f9e5a7 | ||
|
|
81a1601f3c | ||
|
|
2fafc81e06 | ||
|
|
a31e196205 | ||
|
|
02644ab02a | ||
|
|
3bad9f3136 | ||
|
|
534fb98c35 | ||
|
|
624ac8bd8c | ||
|
|
7e8a0d2413 | ||
|
|
89574f5c7a | ||
|
|
0a90614301 | ||
|
|
9e22d17b0e | ||
|
|
f448d357e0 | ||
|
|
9b898a9365 | ||
|
|
30bb73d527 | ||
|
|
346fa152f9 | ||
|
|
ad29964e18 | ||
|
|
4808de10d6 | ||
|
|
53b0e3b6ac | ||
|
|
d6898b1245 | ||
|
|
2014a651f5 | ||
|
|
c73cbdd69e | ||
|
|
4b54ecdf2f | ||
|
|
bfda984aa1 | ||
|
|
5a7da65049 | ||
|
|
d12b26a2ed | ||
|
|
2e766dd0db | ||
|
|
3e30f8eb36 | ||
|
|
7bf0e245c6 | ||
|
|
cf43256b1d | ||
|
|
52e564a4d4 | ||
|
|
15562775d8 | ||
|
|
34f2b3404f | ||
|
|
7984d3dc8c | ||
|
|
a4031262d9 | ||
|
|
61d2b7d151 | ||
|
|
34beaeecd3 | ||
|
|
747bb5a5e0 | ||
|
|
0051c8a6f7 | ||
|
|
bf5d68dae6 | ||
|
|
95abf32e32 | ||
|
|
e8acd2a969 | ||
|
|
b15a4c7435 | ||
|
|
7d795f4dca | ||
|
|
e764548702 | ||
|
|
e3bb1a9f05 | ||
|
|
e3a266863d | ||
|
|
b539ae3e2d | ||
|
|
11f0915771 | ||
|
|
1ff59baea7 | ||
|
|
ef8305d13d | ||
|
|
813d5b9982 | ||
|
|
86063b41dd | ||
|
|
18125068e3 | ||
|
|
6a284fc24c | ||
|
|
62afb42240 | ||
|
|
99b5ec04d2 | ||
|
|
17effeff51 | ||
|
|
4419fdc0df | ||
|
|
80a5544bf2 | ||
|
|
6c23abac48 | ||
|
|
52adf9d190 | ||
|
|
2ee5cb849a | ||
|
|
afd2b9f6d9 | ||
|
|
c21ff40343 | ||
|
|
904ef92505 | ||
|
|
5922f488d7 | ||
|
|
1253f1eebb | ||
|
|
99be18e32f | ||
|
|
a94ef2cacd | ||
|
|
d7878df3da | ||
|
|
7ad0d8ca45 | ||
|
|
4b5b49756d | ||
|
|
c4159ba7a0 | ||
|
|
4db632d669 | ||
|
|
3d7c8e5795 | ||
|
|
26cbf76312 | ||
|
|
92fc4c8aa1 | ||
|
|
4bde16547e | ||
|
|
0cee9d388a | ||
|
|
eef175d247 | ||
|
|
2b44c2fe3d | ||
|
|
ff3145215e | ||
|
|
03cbdb3fae | ||
|
|
d91c191854 | ||
|
|
5ab145d968 | ||
|
|
a8ac2023d5 | ||
|
|
b35e861262 | ||
|
|
e355fde8bf | ||
|
|
9006734293 | ||
|
|
cc85c30c4f | ||
|
|
b41741136d | ||
|
|
b356d39779 | ||
|
|
ffba451dce | ||
|
|
c283b71b42 | ||
|
|
5016d5bd58 | ||
|
|
454cfc929b | ||
|
|
72bfd147f3 | ||
|
|
74cbb6fde3 | ||
|
|
99a96a99bc | ||
|
|
affbdfafcd | ||
|
|
87de29ee47 | ||
|
|
28419d09da | ||
|
|
c1e2a1e9be | ||
|
|
86425f095b | ||
|
|
41a95a64f0 | ||
|
|
e60fa6453c | ||
|
|
15c9e398f1 | ||
|
|
7e560cef77 | ||
|
|
31a83f69a6 | ||
|
|
8783c5fd71 | ||
|
|
bd6c37dcaa | ||
|
|
d8d4dcbda0 | ||
|
|
04863474d2 | ||
|
|
f41a49132f | ||
|
|
6afa454543 | ||
|
|
500c8ad2af | ||
|
|
47294b083f | ||
|
|
ac443ea42f | ||
|
|
c21c824010 | ||
|
|
7b3cab409a | ||
|
|
c9bf25b937 | ||
|
|
54da0f0fb9 | ||
|
|
d5d7a46d45 | ||
|
|
8e6f4e5200 | ||
|
|
4e1518c90a | ||
|
|
d55c7ef173 | ||
|
|
d8665f682b | ||
|
|
d79c10c022 | ||
|
|
5077378c1c | ||
|
|
7265e635b9 | ||
|
|
76dde174d8 | ||
|
|
7b469bc8be | ||
|
|
bbc7de2cbd | ||
|
|
78e4e4ea23 | ||
|
|
1c6164e11c | ||
|
|
23c0cd841a | ||
|
|
dba8e01e54 | ||
|
|
bf26f737fd | ||
|
|
838bcaa843 | ||
|
|
24e096d9f0 | ||
|
|
b255a0d55a | ||
|
|
bbf513839a | ||
|
|
b079fa23a6 | ||
|
|
81fbea44e4 | ||
|
|
afb3405654 | ||
|
|
ef079957aa | ||
|
|
db500be81d | ||
|
|
71602b28de | ||
|
|
9a496c5a6a | ||
|
|
8d7f18dea9 | ||
|
|
8cd5abd0e1 | ||
|
|
3580a36be3 | ||
|
|
65f19b01fa | ||
|
|
42effa5f4e | ||
|
|
4b4f541455 | ||
|
|
327a5d426b | ||
|
|
0bf3fa6bf8 | ||
|
|
402e07827c | ||
|
|
56aba899df | ||
|
|
979d1e9fb4 | ||
|
|
9f1dc4194e | ||
|
|
e8d2b3232e | ||
|
|
8794ebc92f | ||
|
|
d0d631285b | ||
|
|
c5ce0dcddd | ||
|
|
609a80ba0e | ||
|
|
d30f8e5c75 | ||
|
|
cacc7f082f | ||
|
|
f60d3290c5 | ||
|
|
deaad0f8fd | ||
|
|
7ccae06bce | ||
|
|
c250fb9e0d | ||
|
|
53c4b90ffc | ||
|
|
8373f5bfce | ||
|
|
72f242319d | ||
|
|
9a143c5ab2 | ||
|
|
e0cf685e45 | ||
|
|
2be290d146 | ||
|
|
1a8a5dbee2 | ||
|
|
987fd0ade3 | ||
|
|
e592f889c1 | ||
|
|
86fb709c81 | ||
|
|
28e4960780 | ||
|
|
9775aa165f | ||
|
|
a80b506f96 | ||
|
|
e76c9b8082 | ||
|
|
0f78c98629 | ||
|
|
15c3793dab | ||
|
|
0592c72ab3 | ||
|
|
744a3566d6 | ||
|
|
6d8e8c7661 | ||
|
|
fc7e2dd630 | ||
|
|
7c573c684f | ||
|
|
bf70f4636b | ||
|
|
ce20724121 | ||
|
|
9265671d25 | ||
|
|
f144e4c383 | ||
|
|
9c68a385ff | ||
|
|
99c81d291f | ||
|
|
530b637d27 | ||
|
|
e11a646657 | ||
|
|
adb82cae81 | ||
|
|
d961b41607 | ||
|
|
764a4034a2 | ||
|
|
facd216835 | ||
|
|
03e64e043b | ||
|
|
d29b51561b | ||
|
|
2548f41be8 | ||
|
|
4c4736f99b | ||
|
|
aa482a199a | ||
|
|
c04ca640bf | ||
|
|
1c730c0622 | ||
|
|
e1c3b01f68 | ||
|
|
bf1b47be1a | ||
|
|
7dd8f9f481 | ||
|
|
a63162f5a7 | ||
|
|
c714d9c9e9 | ||
|
|
2a5a458172 | ||
|
|
9c26f5ec7b | ||
|
|
684e7b9f9e | ||
|
|
df25aa0a95 | ||
|
|
3c25760cc9 | ||
|
|
65c42b436f | ||
|
|
6ca53d7f6a | ||
|
|
ead64d7770 | ||
|
|
413db08e1a | ||
|
|
2f7d46071d | ||
|
|
7345433d03 | ||
|
|
27ef9f2236 | ||
|
|
b6e514d006 | ||
|
|
2a34efc31d | ||
|
|
9472ee91ed | ||
|
|
5669962393 | ||
|
|
87f244b4fe | ||
|
|
a00df10264 | ||
|
|
8bbca86603 | ||
|
|
be36f71c44 | ||
|
|
6741753643 | ||
|
|
21a85c421a | ||
|
|
a6cc298377 | ||
|
|
011154ba20 | ||
|
|
eaf2c1894d | ||
|
|
f195f7e5fe | ||
|
|
1fca65977d | ||
|
|
342bb13bff | ||
|
|
238c403df9 | ||
|
|
6aa1048d15 | ||
|
|
7bb715cbe7 | ||
|
|
2a0f066426 | ||
|
|
cfbc9ce733 | ||
|
|
a602f57dc4 | ||
|
|
18cf187fda | ||
|
|
c831bf6993 | ||
|
|
2606ba2100 | ||
|
|
c8e2a6636e | ||
|
|
eb527a4c81 | ||
|
|
2c220c87fa | ||
|
|
bc107c28ae | ||
|
|
290aa2ac41 | ||
|
|
9c7cf0f505 | ||
|
|
b386f50203 | ||
|
|
b71f83aa8f | ||
|
|
2754e73d61 | ||
|
|
8f9844eb82 | ||
|
|
a4ddc816e8 | ||
|
|
444ece8c8f | ||
|
|
69876dce00 | ||
|
|
3058b81601 | ||
|
|
1ca8839e53 | ||
|
|
cc7f15fe88 | ||
|
|
b4bef4c034 | ||
|
|
9a47e634b9 | ||
|
|
7e555671fe | ||
|
|
5f268f3d9e | ||
|
|
4ab5a70cdb | ||
|
|
1e2dc36a9f | ||
|
|
2e7ee276f1 | ||
|
|
ac4d303a54 | ||
|
|
09c58ea91d | ||
|
|
4edf2c5c01 | ||
|
|
a36bad8391 | ||
|
|
9a79df8a8a | ||
|
|
993e63c000 | ||
|
|
603f3f1a34 | ||
|
|
43867dfe1a | ||
|
|
26de1b3fa8 | ||
|
|
a25e2153a0 | ||
|
|
cbd8cf8686 | ||
|
|
caae16218e | ||
|
|
17fbe1a4dd | ||
|
|
0f47ccc1ba | ||
|
|
28836f85e5 | ||
|
|
4368401e33 | ||
|
|
9e6470a325 | ||
|
|
08fd80dc15 | ||
|
|
030577f4d7 | ||
|
|
e867ae40ba | ||
|
|
4b865a1955 | ||
|
|
d03e94c4d5 | ||
|
|
8fe520a678 | ||
|
|
97b5997b8b | ||
|
|
c3390a0cb5 | ||
|
|
e4a07f1b89 | ||
|
|
5c5ffb0129 | ||
|
|
1b8ae3ed2e | ||
|
|
57d1157faf | ||
|
|
26a61ecc61 | ||
|
|
4c2feaa1ab | ||
|
|
3b3b366651 | ||
|
|
2eb7068a2d | ||
|
|
f2cd3a85a5 | ||
|
|
ea35a0043c | ||
|
|
2da41efc26 | ||
|
|
d21c6591c1 | ||
|
|
67c96e196e | ||
|
|
2b9771e594 | ||
|
|
80fea67a58 | ||
|
|
f6c2c9f283 | ||
|
|
5d15d83faf | ||
|
|
cb31eb3408 | ||
|
|
38130c20ac | ||
|
|
35a23f6f46 | ||
|
|
55e6a6f58b | ||
|
|
185e5b3865 | ||
|
|
44d4b643f3 | ||
|
|
431a6de992 | ||
|
|
921b8d3cdb | ||
|
|
734cb27345 | ||
|
|
0bf4f95a41 | ||
|
|
d473152c82 | ||
|
|
cffdffb528 | ||
|
|
a641e8c9d3 | ||
|
|
46543952e5 | ||
|
|
7d5daf4747 | ||
|
|
b9a925481e | ||
|
|
39f150ad96 | ||
|
|
7f72a7d28e | ||
|
|
8b7e17d86f | ||
|
|
4935fa7353 | ||
|
|
0cf5bfaaf4 | ||
|
|
f45cb16d4e | ||
|
|
9cfd6bfeea | ||
|
|
f552ca210e | ||
|
|
30ec405890 | ||
|
|
9d2fef093e | ||
|
|
0002cc7c3a | ||
|
|
2729c0d740 | ||
|
|
51d9c1a7c6 | ||
|
|
9b86a6edf4 | ||
|
|
15dc299714 | ||
|
|
1ace12eb12 | ||
|
|
9ea18b8e2b | ||
|
|
3251b60a22 | ||
|
|
c49d85082f | ||
|
|
3fad3bfc5c | ||
|
|
0f4acd346c | ||
|
|
0f82021798 | ||
|
|
703bef2735 | ||
|
|
6e83cf4245 | ||
|
|
b7b24f5a8f | ||
|
|
6fd1eaf492 | ||
|
|
f6bf0e4f4e | ||
|
|
1b3dd93fcc | ||
|
|
083ce17c02 | ||
|
|
5591e42fcf | ||
|
|
a79fc339ae | ||
|
|
959abaca9c | ||
|
|
a06eaf7b95 | ||
|
|
9302045232 | ||
|
|
e2df5f91ad | ||
|
|
2f0b0fb15e | ||
|
|
d44213f6d4 | ||
|
|
c10a755516 | ||
|
|
73df3624f9 | ||
|
|
21c6e66e4f | ||
|
|
4680447dd0 | ||
|
|
5e617d1104 | ||
|
|
729fc8464c | ||
|
|
0e79da03b5 | ||
|
|
70a71b741b | ||
|
|
0990c0ee5e | ||
|
|
9d33ceee33 | ||
|
|
b47b371d7e | ||
|
|
977579e4aa | ||
|
|
643224fd7d | ||
|
|
bb2af14dab | ||
|
|
e908f4b8dc | ||
|
|
07c33192fe | ||
|
|
49eaa39aa2 | ||
|
|
afa8692dbe | ||
|
|
6d1a974f74 | ||
|
|
096aec71e5 | ||
|
|
93a5dc40c7 | ||
|
|
b5f662e26c | ||
|
|
6c73e0d877 | ||
|
|
2b8e62a0d6 | ||
|
|
736cf2fe32 | ||
|
|
c6be3e2ed5 | ||
|
|
327c6b8c53 | ||
|
|
87140dc8b5 | ||
|
|
153d7a4816 | ||
|
|
e0d95a63f3 | ||
|
|
43d81590bb | ||
|
|
ea684537a9 | ||
|
|
61576239cf | ||
|
|
ad7f1bb446 | ||
|
|
72ab35d8d6 | ||
|
|
46655232b6 | ||
|
|
318e22e39f | ||
|
|
8b7f4dc154 | ||
|
|
bbc001e12a | ||
|
|
96dbf1657c | ||
|
|
615f90629c | ||
|
|
434a29a4ee | ||
|
|
660aaae90c | ||
|
|
97fb20e9fd | ||
|
|
1f0144d121 | ||
|
|
6e4ac6e5be | ||
|
|
caad98839f | ||
|
|
d0715b6562 | ||
|
|
25eb268c10 | ||
|
|
9843c250ce | ||
|
|
a51a60b598 | ||
|
|
ecd6e1597f | ||
|
|
8068e418c0 | ||
|
|
2ea95f18e4 | ||
|
|
ce13309d74 | ||
|
|
64f8cbd387 | ||
|
|
e2767e94a7 | ||
|
|
a2b46f3e8e | ||
|
|
e70922b1ce | ||
|
|
92146346bf | ||
|
|
41cb13ada0 | ||
|
|
fb2263a0b1 | ||
|
|
a32e5119fd | ||
|
|
7b4cd6dd2d | ||
|
|
71f1af62c2 | ||
|
|
906b55a5bd | ||
|
|
07d65aa3e6 | ||
|
|
5c0935d253 | ||
|
|
12e51751ee | ||
|
|
af474d4935 | ||
|
|
0a72e578f0 | ||
|
|
8e42b86660 | ||
|
|
854f9cf59e | ||
|
|
45a18307e5 | ||
|
|
ddef346fc3 | ||
|
|
976847dc69 | ||
|
|
ef0af88f23 | ||
|
|
4a1dd020c7 | ||
|
|
6e476b69c7 | ||
|
|
4cbbce39d7 | ||
|
|
fe606a5ba4 | ||
|
|
c87526ea54 | ||
|
|
bfdf0508c0 | ||
|
|
cf268ff510 | ||
|
|
c1283b0976 | ||
|
|
72c13f975f | ||
|
|
23126143be | ||
|
|
7abb07b402 | ||
|
|
f94419ccb0 | ||
|
|
ff3df00e0f | ||
|
|
d845a258de | ||
|
|
4888d07567 | ||
|
|
5acc12852a | ||
|
|
47fa5c78a1 | ||
|
|
b87dc53c39 | ||
|
|
175a3b074f | ||
|
|
4bf897953c | ||
|
|
30be8b9d04 | ||
|
|
c70a2175c0 | ||
|
|
5942905701 | ||
|
|
75db5e854e | ||
|
|
b6ade014a5 | ||
|
|
c4bba32347 | ||
|
|
46bdbd36b5 | ||
|
|
164a84d049 | ||
|
|
3f4c902c6a | ||
|
|
d382623398 | ||
|
|
db6f46b862 | ||
|
|
9c2cae25d0 | ||
|
|
7d34a8fe71 | ||
|
|
e968bb921d | ||
|
|
4931c8ed3b | ||
|
|
e488a6d88d | ||
|
|
f3af6c1fd5 | ||
|
|
397494478a | ||
|
|
80f13dc8ef | ||
|
|
7a1a14418e | ||
|
|
defe5dff68 | ||
|
|
c5d2d2ea5f | ||
|
|
bc81560e3b | ||
|
|
99a25a480b | ||
|
|
8c05ca15b4 | ||
|
|
5e37e14498 | ||
|
|
f815038069 | ||
|
|
fe296e91ba | ||
|
|
64ba1a7cab | ||
|
|
d18aefe4bc | ||
|
|
86a8902872 | ||
|
|
fd891b97d6 | ||
|
|
6839a11685 | ||
|
|
c2a1b71ed4 | ||
|
|
c812915765 | ||
|
|
5e3daaaf11 | ||
|
|
5e21353fca | ||
|
|
c441124228 | ||
|
|
ce6d787ca2 | ||
|
|
38cfc60441 | ||
|
|
fe9ce203ee | ||
|
|
03f5ec3346 | ||
|
|
84fd87c67c | ||
|
|
76494ab0be | ||
|
|
d17f80f86a | ||
|
|
60426226d0 | ||
|
|
fad769fbaa | ||
|
|
758c885973 | ||
|
|
734002eb63 | ||
|
|
949cbd214b | ||
|
|
0d9447c76b | ||
|
|
ce0205bcd4 | ||
|
|
919a131c58 | ||
|
|
34efbd6869 | ||
|
|
23444a7a70 | ||
|
|
0ac89e92ae | ||
|
|
87fcd4537f | ||
|
|
df2413e2d7 | ||
|
|
5e977f42b3 | ||
|
|
7fca1ba2f4 | ||
|
|
3da91deed6 | ||
|
|
6cdc2a8bc7 | ||
|
|
16132b3aaa | ||
|
|
548608490a | ||
|
|
00af220ee1 | ||
|
|
7953078a31 | ||
|
|
0b4715a1b4 | ||
|
|
05ae05ce05 | ||
|
|
6c299f6f62 | ||
|
|
ec92462301 | ||
|
|
71f547219c | ||
|
|
7266508701 | ||
|
|
16f124d385 | ||
|
|
0d9734960e | ||
|
|
e425c1cf59 | ||
|
|
c8f4b4e420 | ||
|
|
4920e69752 | ||
|
|
d5b884f824 | ||
|
|
e68afd7a08 | ||
|
|
0fe3e51115 | ||
|
|
2f641984d9 | ||
|
|
37ac143808 | ||
|
|
0c959c062b | ||
|
|
c6a761a06f | ||
|
|
e40ed2d6de | ||
|
|
752df1681a | ||
|
|
ece216260e | ||
|
|
dcacbf606a | ||
|
|
5bff3e4bc3 | ||
|
|
ce0a47f674 | ||
|
|
48de7ee011 | ||
|
|
c32fb85dbd | ||
|
|
e1387452be | ||
|
|
bb2587664a | ||
|
|
045cc233c8 | ||
|
|
624abefdb0 | ||
|
|
f66eb5a0a3 | ||
|
|
2da8c42a82 | ||
|
|
e6e6ad2e1f | ||
|
|
c9253a8d52 | ||
|
|
703b05187d | ||
|
|
9f59ef7516 | ||
|
|
a69dfe7b20 | ||
|
|
2352ad0bb0 | ||
|
|
deab5888bb | ||
|
|
80ed445604 | ||
|
|
2e591b3923 | ||
|
|
5ddfd922fe | ||
|
|
1a8883fb63 | ||
|
|
17c209da85 | ||
|
|
a8d110ad74 | ||
|
|
d55aa7b715 | ||
|
|
6eca4b7b42 | ||
|
|
0a53d754af | ||
|
|
4188b54c28 | ||
|
|
75432db70d | ||
|
|
38cac4d9a8 | ||
|
|
098e304118 | ||
|
|
e9e884b0ee | ||
|
|
e311cb647f | ||
|
|
48de5711c9 | ||
|
|
b7f4e0fb84 | ||
|
|
5407485f96 | ||
|
|
478012f716 | ||
|
|
d0b952fbc5 | ||
|
|
420631d588 | ||
|
|
a102cc7c8c | ||
|
|
ec46db033a | ||
|
|
7ae657880d | ||
|
|
2446f64e45 | ||
|
|
db67cffb4d | ||
|
|
909cc30181 | ||
|
|
c7bf1369d6 | ||
|
|
c00bc15a2a | ||
|
|
15aea578e0 | ||
|
|
0e60a6235d | ||
|
|
0250a1aa64 | ||
|
|
852166f5cc | ||
|
|
4dc2cccd48 | ||
|
|
96f8e5158e | ||
|
|
dcf112237f | ||
|
|
3368e23a5b | ||
|
|
fcddfab737 | ||
|
|
684a20da1d | ||
|
|
c0b865aa07 | ||
|
|
9bac947c5d | ||
|
|
a0fa4b6bcc | ||
|
|
c5024a837d | ||
|
|
c00f90638d | ||
|
|
d8b3a0220a | ||
|
|
eca5067f2b | ||
|
|
c31e56727a | ||
|
|
63dce92056 | ||
|
|
b599d835aa | ||
|
|
981871c512 | ||
|
|
d4b8088969 | ||
|
|
0fd3ea3b1a | ||
|
|
176edf427e | ||
|
|
255e45ca58 | ||
|
|
87d313e132 | ||
|
|
5f014635e1 | ||
|
|
252e8d57ce | ||
|
|
6b1716a43c | ||
|
|
23fe981cd5 | ||
|
|
68ffef7ae8 | ||
|
|
ef1227777f | ||
|
|
ba97d0e838 | ||
|
|
94c28dd0a0 | ||
|
|
c251b312d8 | ||
|
|
36c3fc1842 | ||
|
|
73a0364d24 | ||
|
|
9bb5274201 | ||
|
|
4ad0e4c24c | ||
|
|
cf7a5b9b7c | ||
|
|
d07b14657e | ||
|
|
e9e57ebc4d | ||
|
|
61c2994880 | ||
|
|
629d2b8af4 | ||
|
|
8b7486f5c0 | ||
|
|
471024f7b7 | ||
|
|
d6dca124a7 | ||
|
|
d9f8719f51 | ||
|
|
be014c1b23 | ||
|
|
6bd42e1d92 | ||
|
|
efac35a337 | ||
|
|
d297393649 | ||
|
|
93df8edcf6 | ||
|
|
8cc5a0802d | ||
|
|
b1c272b893 | ||
|
|
9f0e201537 | ||
|
|
753f81a9f2 | ||
|
|
8162b0833c | ||
|
|
93e322a9a0 | ||
|
|
8e0cd2fd3b | ||
|
|
28b00c1086 | ||
|
|
8a760b4bb5 | ||
|
|
3b9ebd43db | ||
|
|
4a145a3415 | ||
|
|
f558179f1e | ||
|
|
3e4854e7e6 | ||
|
|
bc57d65c36 | ||
|
|
5bbdee5b1d | ||
|
|
df363ff0c3 | ||
|
|
d417e4b8aa | ||
|
|
7b9791660d | ||
|
|
8cd9767a99 | ||
|
|
70467635f7 | ||
|
|
83ceffbf87 | ||
|
|
c449945b6c | ||
|
|
161a8e8184 | ||
|
|
af90994b1e | ||
|
|
1aebd13649 | ||
|
|
c43d143744 | ||
|
|
fd1a382f99 | ||
|
|
e1c419d845 | ||
|
|
ab03b47fc3 | ||
|
|
70a192348f | ||
|
|
0bae1a40d1 | ||
|
|
5a8384a048 | ||
|
|
1d87a0a350 | ||
|
|
55ed5a1153 | ||
|
|
01c20c31c2 | ||
|
|
4479d4000e | ||
|
|
edd511f06e | ||
|
|
8ef3f520b1 | ||
|
|
ab3492bb90 | ||
|
|
ed7cbb3866 | ||
|
|
a30626cfe3 | ||
|
|
d2c087b3e2 | ||
|
|
0a747f991d | ||
|
|
f5a1e86d85 | ||
|
|
693bcb0f83 | ||
|
|
0d0daed86e | ||
|
|
95a349abcc | ||
|
|
c6ba5dc539 | ||
|
|
6c3afeec0f | ||
|
|
29c58b4588 | ||
|
|
ecc565aa3f | ||
|
|
db61810f98 | ||
|
|
48f203ad5b | ||
|
|
6ab4ac2dfb | ||
|
|
9cb3c7b6e6 | ||
|
|
adaa4ed350 | ||
|
|
a64b9aa9b8 | ||
|
|
0138eca61d | ||
|
|
139a40de29 | ||
|
|
17d9becd3c | ||
|
|
449b482cc3 | ||
|
|
a7e099c946 | ||
|
|
610462be5a | ||
|
|
207d32668f | ||
|
|
e94a30bec0 |
25
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
25
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve openpilot
|
||||
title: ''
|
||||
labels: 'bug'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**How to reproduce or log data**
|
||||
Steps to reproduce the behavior, or a explorer/cabana link to the exact drive and timestamp of when the bug occurred.
|
||||
|
||||
**Expected behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
** Device/Version information (please complete the following information):**
|
||||
- Device: [e.g. EON/EON Gold]
|
||||
- Version: [e.g. 0.6.4], or commit hash when on devel
|
||||
- Car make/model [e.g. Toyota Prius 2016]
|
||||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
21
.github/pull_request_template.md
vendored
Normal file
21
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
Choose one of the templates below:
|
||||
|
||||
# Fingerprint
|
||||
This pull requests adds a fingerprint for <Make - Model - Year - Trim>.
|
||||
|
||||
This is an explorer link to a drive with the stock system enabled: ...
|
||||
|
||||
# Car support
|
||||
This pull requests adds support for <Make - Model - Year - Trim>.
|
||||
|
||||
This is an explorer link to a drive with the stock system enabled: ...
|
||||
This is an explorer link to a drive with openpilot system enabled: ...
|
||||
|
||||
# Feature
|
||||
This pull requests adds feature X
|
||||
|
||||
## Description
|
||||
Explain what the feature does
|
||||
|
||||
## Testing
|
||||
Explain how the feature was tested. Either by the added unit tests, or what tests were performed while driving.
|
||||
47
.gitignore
vendored
47
.gitignore
vendored
@@ -1 +1,48 @@
|
||||
venv/
|
||||
.DS_Store
|
||||
.tags
|
||||
.ipynb_checkpoints
|
||||
.idea
|
||||
.sconsign.dblite
|
||||
.vscode
|
||||
model2.png
|
||||
a.out
|
||||
|
||||
*.DSYM
|
||||
*.d
|
||||
*.pyc
|
||||
*.pyo
|
||||
.*.swp
|
||||
.*.swo
|
||||
.*.un~
|
||||
*.o
|
||||
*.so
|
||||
*.a
|
||||
*.clb
|
||||
*.class
|
||||
*.pyxbldc
|
||||
*.vcd
|
||||
config.json
|
||||
clcache
|
||||
|
||||
board/obj/
|
||||
selfdrive/boardd/boardd
|
||||
selfdrive/logcatd/logcatd
|
||||
selfdrive/mapd/default_speeds_by_region.json
|
||||
selfdrive/proclogd/proclogd
|
||||
selfdrive/ui/ui
|
||||
selfdrive/test/longitudinal_maneuvers/out
|
||||
selfdrive/visiond/visiond
|
||||
selfdrive/loggerd/loggerd
|
||||
selfdrive/sensord/gpsd
|
||||
selfdrive/sensord/sensord
|
||||
/src/
|
||||
|
||||
one
|
||||
openpilot
|
||||
notebooks
|
||||
xx
|
||||
|
||||
.coverage*
|
||||
htmlcov
|
||||
|
||||
|
||||
585
.pylintrc
Normal file
585
.pylintrc
Normal file
@@ -0,0 +1,585 @@
|
||||
[MASTER]
|
||||
|
||||
# A comma-separated list of package or module names from where C extensions may
|
||||
# be loaded. Extensions are loading into the active Python interpreter and may
|
||||
# run arbitrary code
|
||||
extension-pkg-whitelist=scipy
|
||||
|
||||
# Add files or directories to the blacklist. They should be base names, not
|
||||
# paths.
|
||||
ignore=CVS
|
||||
|
||||
# Add files or directories matching the regex patterns to the blacklist. The
|
||||
# regex matches against base names, not paths.
|
||||
ignore-patterns=
|
||||
|
||||
# Python code to execute, usually for sys.path manipulation such as
|
||||
# pygtk.require().
|
||||
#init-hook=
|
||||
|
||||
# Use multiple processes to speed up Pylint.
|
||||
jobs=4
|
||||
|
||||
# List of plugins (as comma separated values of python modules names) to load,
|
||||
# usually to register additional checkers.
|
||||
load-plugins=
|
||||
|
||||
# Pickle collected data for later comparisons.
|
||||
persistent=yes
|
||||
|
||||
# Specify a configuration file.
|
||||
#rcfile=
|
||||
|
||||
# When enabled, pylint would attempt to guess common misconfiguration and emit
|
||||
# user-friendly hints instead of false-positive error messages
|
||||
suggestion-mode=yes
|
||||
|
||||
# Allow loading of arbitrary C extensions. Extensions are imported into the
|
||||
# active Python interpreter and may run arbitrary code.
|
||||
unsafe-load-any-extension=no
|
||||
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
|
||||
# Only show warnings with the listed confidence levels. Leave empty to show
|
||||
# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED
|
||||
confidence=
|
||||
|
||||
# Disable the message, report, category or checker with the given id(s). You
|
||||
# can either give multiple identifiers separated by comma (,) or put this
|
||||
# option multiple times (only on the command line, not in the configuration
|
||||
# file where it should appear only once).You can also use "--disable=all" to
|
||||
# disable everything first and then reenable specific checks. For example, if
|
||||
# you want to run only the similarities checker, you can use "--disable=all
|
||||
# --enable=similarities". If you want to run only the classes checker, but have
|
||||
# no Warning level messages displayed, use"--disable=all --enable=classes
|
||||
# --disable=W"
|
||||
disable=print-statement,
|
||||
parameter-unpacking,
|
||||
unpacking-in-except,
|
||||
old-raise-syntax,
|
||||
backtick,
|
||||
long-suffix,
|
||||
old-ne-operator,
|
||||
old-octal-literal,
|
||||
import-star-module-level,
|
||||
non-ascii-bytes-literal,
|
||||
raw-checker-failed,
|
||||
bad-inline-option,
|
||||
locally-disabled,
|
||||
locally-enabled,
|
||||
file-ignored,
|
||||
suppressed-message,
|
||||
useless-suppression,
|
||||
deprecated-pragma,
|
||||
apply-builtin,
|
||||
basestring-builtin,
|
||||
buffer-builtin,
|
||||
cmp-builtin,
|
||||
coerce-builtin,
|
||||
execfile-builtin,
|
||||
file-builtin,
|
||||
long-builtin,
|
||||
raw_input-builtin,
|
||||
reduce-builtin,
|
||||
standarderror-builtin,
|
||||
unicode-builtin,
|
||||
xrange-builtin,
|
||||
coerce-method,
|
||||
delslice-method,
|
||||
getslice-method,
|
||||
setslice-method,
|
||||
no-absolute-import,
|
||||
old-division,
|
||||
dict-iter-method,
|
||||
dict-view-method,
|
||||
next-method-called,
|
||||
metaclass-assignment,
|
||||
indexing-exception,
|
||||
raising-string,
|
||||
reload-builtin,
|
||||
oct-method,
|
||||
hex-method,
|
||||
nonzero-method,
|
||||
cmp-method,
|
||||
input-builtin,
|
||||
round-builtin,
|
||||
intern-builtin,
|
||||
unichr-builtin,
|
||||
map-builtin-not-iterating,
|
||||
zip-builtin-not-iterating,
|
||||
range-builtin-not-iterating,
|
||||
filter-builtin-not-iterating,
|
||||
using-cmp-argument,
|
||||
eq-without-hash,
|
||||
div-method,
|
||||
idiv-method,
|
||||
rdiv-method,
|
||||
exception-message-attribute,
|
||||
invalid-str-codec,
|
||||
sys-max-int,
|
||||
bad-python3-import,
|
||||
deprecated-string-function,
|
||||
deprecated-str-translate-call,
|
||||
deprecated-itertools-function,
|
||||
deprecated-types-field,
|
||||
next-method-defined,
|
||||
dict-items-not-iterating,
|
||||
dict-keys-not-iterating,
|
||||
dict-values-not-iterating,
|
||||
bad-indentation,
|
||||
line-too-long,
|
||||
missing-docstring,
|
||||
multiple-statements,
|
||||
bad-continuation,
|
||||
invalid-name,
|
||||
too-many-arguments,
|
||||
too-many-locals,
|
||||
superfluous-parens,
|
||||
bad-whitespace,
|
||||
too-many-instance-attributes,
|
||||
wrong-import-position,
|
||||
ungrouped-imports,
|
||||
wrong-import-order,
|
||||
protected-access,
|
||||
trailing-whitespace,
|
||||
too-many-branches,
|
||||
too-few-public-methods,
|
||||
too-many-statements,
|
||||
trailing-newlines,
|
||||
attribute-defined-outside-init,
|
||||
too-many-return-statements,
|
||||
too-many-public-methods,
|
||||
unused-argument,
|
||||
old-style-class,
|
||||
no-init,
|
||||
len-as-condition,
|
||||
unneeded-not,
|
||||
no-self-use,
|
||||
multiple-imports,
|
||||
no-else-return,
|
||||
logging-not-lazy,
|
||||
fixme,
|
||||
redefined-outer-name,
|
||||
unused-variable,
|
||||
unsubscriptable-object,
|
||||
expression-not-assigned,
|
||||
too-many-boolean-expressions,
|
||||
consider-using-ternary,
|
||||
invalid-unary-operand-type,
|
||||
relative-import,
|
||||
deprecated-lambda
|
||||
|
||||
|
||||
# Enable the message, report, category or checker with the given id(s). You can
|
||||
# either give multiple identifier separated by comma (,) or put this option
|
||||
# multiple time (only on the command line, not in the configuration file where
|
||||
# it should appear only once). See also the "--disable" option for examples.
|
||||
enable=c-extension-no-member
|
||||
|
||||
|
||||
[REPORTS]
|
||||
|
||||
# Python expression which should return a note less than 10 (10 is the highest
|
||||
# note). You have access to the variables errors warning, statement which
|
||||
# respectively contain the number of errors / warnings messages and the total
|
||||
# number of statements analyzed. This is used by the global evaluation report
|
||||
# (RP0004).
|
||||
evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
|
||||
|
||||
# Template used to display messages. This is a python new-style format string
|
||||
# used to format the message information. See doc for all details
|
||||
#msg-template=
|
||||
|
||||
# Set the output format. Available formats are text, parseable, colorized, json
|
||||
# and msvs (visual studio).You can also give a reporter class, eg
|
||||
# mypackage.mymodule.MyReporterClass.
|
||||
output-format=text
|
||||
|
||||
# Tells whether to display a full report or only the messages
|
||||
reports=no
|
||||
|
||||
# Activate the evaluation score.
|
||||
score=yes
|
||||
|
||||
|
||||
[REFACTORING]
|
||||
|
||||
# Maximum number of nested blocks for function / method body
|
||||
max-nested-blocks=5
|
||||
|
||||
# Complete name of functions that never returns. When checking for
|
||||
# inconsistent-return-statements if a never returning function is called then
|
||||
# it will be considered as an explicit return statement and no message will be
|
||||
# printed.
|
||||
never-returning-functions=optparse.Values,sys.exit
|
||||
|
||||
|
||||
[LOGGING]
|
||||
|
||||
# Logging modules to check that the string format arguments are in logging
|
||||
# function parameter format
|
||||
logging-modules=logging
|
||||
|
||||
|
||||
[SPELLING]
|
||||
|
||||
# Limits count of emitted suggestions for spelling mistakes
|
||||
max-spelling-suggestions=4
|
||||
|
||||
# Spelling dictionary name. Available dictionaries: none. To make it working
|
||||
# install python-enchant package.
|
||||
spelling-dict=
|
||||
|
||||
# List of comma separated words that should not be checked.
|
||||
spelling-ignore-words=
|
||||
|
||||
# A path to a file that contains private dictionary; one word per line.
|
||||
spelling-private-dict-file=
|
||||
|
||||
# Tells whether to store unknown words to indicated private dictionary in
|
||||
# --spelling-private-dict-file option instead of raising a message.
|
||||
spelling-store-unknown-words=no
|
||||
|
||||
|
||||
[MISCELLANEOUS]
|
||||
|
||||
# List of note tags to take in consideration, separated by a comma.
|
||||
notes=FIXME,
|
||||
XXX,
|
||||
TODO
|
||||
|
||||
|
||||
[SIMILARITIES]
|
||||
|
||||
# Ignore comments when computing similarities.
|
||||
ignore-comments=yes
|
||||
|
||||
# Ignore docstrings when computing similarities.
|
||||
ignore-docstrings=yes
|
||||
|
||||
# Ignore imports when computing similarities.
|
||||
ignore-imports=no
|
||||
|
||||
# Minimum lines number of a similarity.
|
||||
min-similarity-lines=4
|
||||
|
||||
|
||||
[TYPECHECK]
|
||||
|
||||
# List of decorators that produce context managers, such as
|
||||
# contextlib.contextmanager. Add to this list to register other decorators that
|
||||
# produce valid context managers.
|
||||
contextmanager-decorators=contextlib.contextmanager
|
||||
|
||||
# List of members which are set dynamically and missed by pylint inference
|
||||
# system, and so shouldn't trigger E1101 when accessed. Python regular
|
||||
# expressions are accepted.
|
||||
generated-members=capnp.* cereal.* pygame.* zmq.* setproctitle.* smbus2.* usb1.* serial.* cv2.*
|
||||
|
||||
# Tells whether missing members accessed in mixin class should be ignored. A
|
||||
# mixin class is detected if its name ends with "mixin" (case insensitive).
|
||||
ignore-mixin-members=yes
|
||||
|
||||
# This flag controls whether pylint should warn about no-member and similar
|
||||
# checks whenever an opaque object is returned when inferring. The inference
|
||||
# can return multiple potential results while evaluating a Python object, but
|
||||
# some branches might not be evaluated, which results in partial inference. In
|
||||
# that case, it might be useful to still emit no-member and other checks for
|
||||
# the rest of the inferred objects.
|
||||
ignore-on-opaque-inference=yes
|
||||
|
||||
# List of class names for which member attributes should not be checked (useful
|
||||
# for classes with dynamically set attributes). This supports the use of
|
||||
# qualified names.
|
||||
ignored-classes=optparse.Values,thread._local,_thread._local
|
||||
|
||||
# List of module names for which member attributes should not be checked
|
||||
# (useful for modules/projects where namespaces are manipulated during runtime
|
||||
# and thus existing member attributes cannot be deduced by static analysis. It
|
||||
# supports qualified module names, as well as Unix pattern matching.
|
||||
ignored-modules=flask setproctitle usb1 flask.ext.socketio smbus2 usb1.*
|
||||
|
||||
# Show a hint with possible names when a member name was not found. The aspect
|
||||
# of finding the hint is based on edit distance.
|
||||
missing-member-hint=yes
|
||||
|
||||
# The minimum edit distance a name should have in order to be considered a
|
||||
# similar match for a missing member name.
|
||||
missing-member-hint-distance=1
|
||||
|
||||
# The total number of similar names that should be taken in consideration when
|
||||
# showing a hint for a missing member.
|
||||
missing-member-max-choices=1
|
||||
|
||||
|
||||
[VARIABLES]
|
||||
|
||||
# List of additional names supposed to be defined in builtins. Remember that
|
||||
# you should avoid to define new builtins when possible.
|
||||
additional-builtins=
|
||||
|
||||
# Tells whether unused global variables should be treated as a violation.
|
||||
allow-global-unused-variables=yes
|
||||
|
||||
# List of strings which can identify a callback function by name. A callback
|
||||
# name must start or end with one of those strings.
|
||||
callbacks=cb_,
|
||||
_cb
|
||||
|
||||
# A regular expression matching the name of dummy variables (i.e. expectedly
|
||||
# not used).
|
||||
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
|
||||
|
||||
# Argument names that match this expression will be ignored. Default to name
|
||||
# with leading underscore
|
||||
ignored-argument-names=_.*|^ignored_|^unused_
|
||||
|
||||
# Tells whether we should check for unused import in __init__ files.
|
||||
init-import=no
|
||||
|
||||
# List of qualified module names which can have objects that can redefine
|
||||
# builtins.
|
||||
redefining-builtins-modules=six.moves,past.builtins,future.builtins
|
||||
|
||||
|
||||
[FORMAT]
|
||||
|
||||
# Expected format of line ending, e.g. empty (any line ending), LF or CRLF.
|
||||
expected-line-ending-format=
|
||||
|
||||
# Regexp for a line that is allowed to be longer than the limit.
|
||||
ignore-long-lines=^\s*(# )?<?https?://\S+>?$
|
||||
|
||||
# Number of spaces of indent required inside a hanging or continued line.
|
||||
indent-after-paren=4
|
||||
|
||||
# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
|
||||
# tab).
|
||||
indent-string=' '
|
||||
|
||||
# Maximum number of characters on a single line.
|
||||
max-line-length=100
|
||||
|
||||
# Maximum number of lines in a module
|
||||
max-module-lines=1000
|
||||
|
||||
# List of optional constructs for which whitespace checking is disabled. `dict-
|
||||
# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}.
|
||||
# `trailing-comma` allows a space between comma and closing bracket: (a, ).
|
||||
# `empty-line` allows space-only lines.
|
||||
no-space-check=trailing-comma,
|
||||
dict-separator
|
||||
|
||||
# Allow the body of a class to be on the same line as the declaration if body
|
||||
# contains single statement.
|
||||
single-line-class-stmt=no
|
||||
|
||||
# Allow the body of an if to be on the same line as the test if there is no
|
||||
# else.
|
||||
single-line-if-stmt=no
|
||||
|
||||
|
||||
[BASIC]
|
||||
|
||||
# Naming style matching correct argument names
|
||||
argument-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct argument names. Overrides argument-
|
||||
# naming-style
|
||||
#argument-rgx=
|
||||
|
||||
# Naming style matching correct attribute names
|
||||
attr-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct attribute names. Overrides attr-naming-
|
||||
# style
|
||||
#attr-rgx=
|
||||
|
||||
# Bad variable names which should always be refused, separated by a comma
|
||||
bad-names=foo,
|
||||
bar,
|
||||
baz,
|
||||
toto,
|
||||
tutu,
|
||||
tata
|
||||
|
||||
# Naming style matching correct class attribute names
|
||||
class-attribute-naming-style=any
|
||||
|
||||
# Regular expression matching correct class attribute names. Overrides class-
|
||||
# attribute-naming-style
|
||||
#class-attribute-rgx=
|
||||
|
||||
# Naming style matching correct class names
|
||||
class-naming-style=PascalCase
|
||||
|
||||
# Regular expression matching correct class names. Overrides class-naming-style
|
||||
#class-rgx=
|
||||
|
||||
# Naming style matching correct constant names
|
||||
const-naming-style=UPPER_CASE
|
||||
|
||||
# Regular expression matching correct constant names. Overrides const-naming-
|
||||
# style
|
||||
#const-rgx=
|
||||
|
||||
# Minimum line length for functions/classes that require docstrings, shorter
|
||||
# ones are exempt.
|
||||
docstring-min-length=-1
|
||||
|
||||
# Naming style matching correct function names
|
||||
function-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct function names. Overrides function-
|
||||
# naming-style
|
||||
#function-rgx=
|
||||
|
||||
# Good variable names which should always be accepted, separated by a comma
|
||||
good-names=i,
|
||||
j,
|
||||
k,
|
||||
ex,
|
||||
Run,
|
||||
_
|
||||
|
||||
# Include a hint for the correct naming format with invalid-name
|
||||
include-naming-hint=no
|
||||
|
||||
# Naming style matching correct inline iteration names
|
||||
inlinevar-naming-style=any
|
||||
|
||||
# Regular expression matching correct inline iteration names. Overrides
|
||||
# inlinevar-naming-style
|
||||
#inlinevar-rgx=
|
||||
|
||||
# Naming style matching correct method names
|
||||
method-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct method names. Overrides method-naming-
|
||||
# style
|
||||
#method-rgx=
|
||||
|
||||
# Naming style matching correct module names
|
||||
module-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct module names. Overrides module-naming-
|
||||
# style
|
||||
#module-rgx=
|
||||
|
||||
# Colon-delimited sets of names that determine each other's naming style when
|
||||
# the name regexes allow several styles.
|
||||
name-group=
|
||||
|
||||
# Regular expression which should only match function or class names that do
|
||||
# not require a docstring.
|
||||
no-docstring-rgx=^_
|
||||
|
||||
# List of decorators that produce properties, such as abc.abstractproperty. Add
|
||||
# to this list to register other decorators that produce valid properties.
|
||||
property-classes=abc.abstractproperty
|
||||
|
||||
# Naming style matching correct variable names
|
||||
variable-naming-style=snake_case
|
||||
|
||||
# Regular expression matching correct variable names. Overrides variable-
|
||||
# naming-style
|
||||
#variable-rgx=
|
||||
|
||||
|
||||
[DESIGN]
|
||||
|
||||
# Maximum number of arguments for function / method
|
||||
max-args=5
|
||||
|
||||
# Maximum number of attributes for a class (see R0902).
|
||||
max-attributes=7
|
||||
|
||||
# Maximum number of boolean expressions in a if statement
|
||||
max-bool-expr=5
|
||||
|
||||
# Maximum number of branch for function / method body
|
||||
max-branches=12
|
||||
|
||||
# Maximum number of locals for function / method body
|
||||
max-locals=15
|
||||
|
||||
# Maximum number of parents for a class (see R0901).
|
||||
max-parents=7
|
||||
|
||||
# Maximum number of public methods for a class (see R0904).
|
||||
max-public-methods=20
|
||||
|
||||
# Maximum number of return / yield for function / method body
|
||||
max-returns=6
|
||||
|
||||
# Maximum number of statements in function / method body
|
||||
max-statements=50
|
||||
|
||||
# Minimum number of public methods for a class (see R0903).
|
||||
min-public-methods=2
|
||||
|
||||
|
||||
[CLASSES]
|
||||
|
||||
# List of method names used to declare (i.e. assign) instance attributes.
|
||||
defining-attr-methods=__init__,
|
||||
__new__,
|
||||
setUp
|
||||
|
||||
# List of member names, which should be excluded from the protected access
|
||||
# warning.
|
||||
exclude-protected=_asdict,
|
||||
_fields,
|
||||
_replace,
|
||||
_source,
|
||||
_make
|
||||
|
||||
# List of valid names for the first argument in a class method.
|
||||
valid-classmethod-first-arg=cls
|
||||
|
||||
# List of valid names for the first argument in a metaclass class method.
|
||||
valid-metaclass-classmethod-first-arg=mcs
|
||||
|
||||
|
||||
[IMPORTS]
|
||||
|
||||
# Allow wildcard imports from modules that define __all__.
|
||||
allow-wildcard-with-all=no
|
||||
|
||||
# Analyse import fallback blocks. This can be used to support both Python 2 and
|
||||
# 3 compatible code, which means that the block might have code that exists
|
||||
# only in one or another interpreter, leading to false positives when analysed.
|
||||
analyse-fallback-blocks=no
|
||||
|
||||
# Deprecated modules which should not be used, separated by a comma
|
||||
deprecated-modules=regsub,
|
||||
TERMIOS,
|
||||
Bastion,
|
||||
rexec
|
||||
|
||||
# Create a graph of external dependencies in the given file (report RP0402 must
|
||||
# not be disabled)
|
||||
ext-import-graph=
|
||||
|
||||
# Create a graph of every (i.e. internal and external) dependencies in the
|
||||
# given file (report RP0402 must not be disabled)
|
||||
import-graph=
|
||||
|
||||
# Create a graph of internal dependencies in the given file (report RP0402 must
|
||||
# not be disabled)
|
||||
int-import-graph=
|
||||
|
||||
# Force import order to recognize a module as part of the standard
|
||||
# compatibility libraries.
|
||||
known-standard-library=
|
||||
|
||||
# Force import order to recognize a module as part of a third party library.
|
||||
known-third-party=enchant
|
||||
|
||||
|
||||
[EXCEPTIONS]
|
||||
|
||||
# Exceptions that will emit a warning when being caught. Defaults to
|
||||
# "Exception"
|
||||
overgeneral-exceptions=Exception
|
||||
7
.travis.yml
Normal file
7
.travis.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
sudo: required
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
script:
|
||||
- ./run_docker_tests.sh
|
||||
31
CONTRIBUTING.md
Normal file
31
CONTRIBUTING.md
Normal file
@@ -0,0 +1,31 @@
|
||||
# How to contribute
|
||||
|
||||
Our software is open source so you can solve your own problems without needing help from others. And if you solve a problem and are so kind, you can upstream it for the rest of the world to use.
|
||||
|
||||
Most open source development activity is coordinated through our [Discord](https://discord.comma.ai). A lot of documentation is available on our [medium](https://medium.com/@comma_ai/)
|
||||
|
||||
## Getting Started
|
||||
|
||||
* Join our [Discord](https://discord.comma.ai)
|
||||
* Make sure you have a [GitHub account](https://github.com/signup/free)
|
||||
* Fork [our repositories](https://github.com/commaai) on GitHub
|
||||
|
||||
## Testing
|
||||
|
||||
### Local Testing
|
||||
|
||||
You can test your changes on your machine by running `run_docker_tests.sh`. This will run some automated tests in docker against your code.
|
||||
|
||||
### Automated Testing
|
||||
|
||||
All PRs are automatically checked by travis. Check out `.travis.yml` for what travis runs. Any new tests sould be added to travis.
|
||||
|
||||
### Code Style and Linting
|
||||
|
||||
Code is automatically check for style by travis as part of the automated tests. You can also run these yourself by running `check_code_quality.sh`.
|
||||
|
||||
## Car Ports (openpilot)
|
||||
|
||||
We've released a [Model Port guide](https://medium.com/@comma_ai/openpilot-port-guide-for-toyota-models-e5467f4b5fe6) for porting to Toyota/Lexus models.
|
||||
|
||||
If you port openpilot to a substantially new car brand, see this more generic [Brand Port guide](https://medium.com/@comma_ai/how-to-write-a-car-port-for-openpilot-7ce0785eda84). You might also be eligible for a bounty. See our bounties at [comma.ai/bounties.html](https://comma.ai/bounties.html)
|
||||
80
Dockerfile.openpilot
Normal file
80
Dockerfile.openpilot
Normal file
@@ -0,0 +1,80 @@
|
||||
FROM ubuntu:16.04
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
|
||||
RUN apt-get update && apt-get install -y \
|
||||
autoconf \
|
||||
build-essential \
|
||||
bzip2 \
|
||||
clang \
|
||||
cmake \
|
||||
curl \
|
||||
ffmpeg \
|
||||
git \
|
||||
libarchive-dev \
|
||||
libbz2-dev \
|
||||
libcurl4-openssl-dev \
|
||||
libeigen3-dev \
|
||||
libffi-dev \
|
||||
libglew-dev \
|
||||
libglib2.0-0 \
|
||||
liblzma-dev \
|
||||
libmysqlclient-dev \
|
||||
libomp-dev \
|
||||
libopencv-dev \
|
||||
libssl-dev \
|
||||
libtool \
|
||||
libusb-1.0-0 \
|
||||
libzmq5-dev \
|
||||
locales \
|
||||
ocl-icd-libopencl1 \
|
||||
ocl-icd-opencl-dev \
|
||||
opencl-headers \
|
||||
python-dev \
|
||||
python-pip \
|
||||
screen \
|
||||
sudo \
|
||||
vim \
|
||||
wget
|
||||
|
||||
|
||||
RUN sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
|
||||
ENV LANG en_US.UTF-8
|
||||
ENV LANGUAGE en_US:en
|
||||
ENV LC_ALL en_US.UTF-8
|
||||
|
||||
RUN curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash
|
||||
|
||||
ENV PATH="/root/.pyenv/bin:/root/.pyenv/shims:${PATH}"
|
||||
RUN pyenv install 3.7.3
|
||||
RUN pyenv global 3.7.3
|
||||
RUN pyenv rehash
|
||||
|
||||
RUN pip install pipenv==2018.11.26
|
||||
|
||||
COPY Pipfile /tmp/
|
||||
COPY Pipfile.lock /tmp/
|
||||
|
||||
RUN python --version
|
||||
RUN cd /tmp && pipenv install --system --deploy
|
||||
|
||||
# Install subset of dev dependencies needed for CI
|
||||
RUN pip install matplotlib==3.1.1 dictdiffer==0.8.0 fastcluster==1.1.25 aenum==2.2.1 scipy==1.3.1 lru-dict==1.1.6 tenacity==5.1.1 azure-common==1.1.23 azure-nspkg==3.0.2 azure-storage-blob==2.1.0 azure-storage-common==2.1.0 azure-storage-nspkg==3.1.0 pycurl==7.43.0.3
|
||||
|
||||
COPY phonelibs/install_capnp.sh /tmp/install_capnp.sh
|
||||
RUN /tmp/install_capnp.sh
|
||||
|
||||
RUN git clone --branch v0.6.5 https://github.com/commaai/openpilot-tools.git /tmp/openpilot/tools
|
||||
|
||||
ENV PYTHONPATH /tmp/openpilot:${PYTHONPATH}
|
||||
COPY ./.pylintrc /tmp/openpilot/.pylintrc
|
||||
COPY ./common /tmp/openpilot/common
|
||||
COPY ./cereal /tmp/openpilot/cereal
|
||||
COPY ./opendbc /tmp/openpilot/opendbc
|
||||
COPY ./selfdrive /tmp/openpilot/selfdrive
|
||||
COPY ./phonelibs /tmp/openpilot/phonelibs
|
||||
COPY ./pyextra /tmp/openpilot/pyextra
|
||||
COPY ./panda /tmp/openpilot/panda
|
||||
|
||||
RUN mkdir -p /tmp/openpilot/selfdrive/test/out
|
||||
RUN make -C /tmp/openpilot/selfdrive/controls/lib/longitudinal_mpc clean
|
||||
RUN make -C /tmp/openpilot/selfdrive/controls/lib/lateral_mpc clean
|
||||
@@ -1,103 +0,0 @@
|
||||
Metadata-Version: 1.1
|
||||
Name: Flask
|
||||
Version: 1.0.2
|
||||
Summary: A simple framework for building complex web applications.
|
||||
Home-page: https://www.palletsprojects.com/p/flask/
|
||||
Author: Pallets team
|
||||
Author-email: contact@palletsprojects.com
|
||||
License: BSD
|
||||
Description: Flask
|
||||
=====
|
||||
|
||||
Flask is a lightweight `WSGI`_ web application framework. It is designed
|
||||
to make getting started quick and easy, with the ability to scale up to
|
||||
complex applications. It began as a simple wrapper around `Werkzeug`_
|
||||
and `Jinja`_ and has become one of the most popular Python web
|
||||
application frameworks.
|
||||
|
||||
Flask offers suggestions, but doesn't enforce any dependencies or
|
||||
project layout. It is up to the developer to choose the tools and
|
||||
libraries they want to use. There are many extensions provided by the
|
||||
community that make adding new functionality easy.
|
||||
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
Install and update using `pip`_:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
pip install -U Flask
|
||||
|
||||
|
||||
A Simple Example
|
||||
----------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from flask import Flask
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/')
|
||||
def hello():
|
||||
return 'Hello, World!'
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
$ FLASK_APP=hello.py flask run
|
||||
* Serving Flask app "hello"
|
||||
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
|
||||
|
||||
|
||||
Donate
|
||||
------
|
||||
|
||||
The Pallets organization develops and supports Flask and the libraries
|
||||
it uses. In order to grow the community of contributors and users, and
|
||||
allow the maintainers to devote more time to the projects, `please
|
||||
donate today`_.
|
||||
|
||||
.. _please donate today: https://psfmember.org/civicrm/contribute/transact?reset=1&id=20
|
||||
|
||||
|
||||
Links
|
||||
-----
|
||||
|
||||
* Website: https://www.palletsprojects.com/p/flask/
|
||||
* Documentation: http://flask.pocoo.org/docs/
|
||||
* License: `BSD <https://github.com/pallets/flask/blob/master/LICENSE>`_
|
||||
* Releases: https://pypi.org/project/Flask/
|
||||
* Code: https://github.com/pallets/flask
|
||||
* Issue tracker: https://github.com/pallets/flask/issues
|
||||
* Test status:
|
||||
|
||||
* Linux, Mac: https://travis-ci.org/pallets/flask
|
||||
* Windows: https://ci.appveyor.com/project/pallets/flask
|
||||
|
||||
* Test coverage: https://codecov.io/gh/pallets/flask
|
||||
|
||||
.. _WSGI: https://wsgi.readthedocs.io
|
||||
.. _Werkzeug: https://www.palletsprojects.com/p/werkzeug/
|
||||
.. _Jinja: https://www.palletsprojects.com/p/jinja/
|
||||
.. _pip: https://pip.pypa.io/en/stable/quickstart/
|
||||
|
||||
Platform: any
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Environment :: Web Environment
|
||||
Classifier: Framework :: Flask
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
||||
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application
|
||||
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
@@ -1,223 +0,0 @@
|
||||
AUTHORS
|
||||
CHANGES.rst
|
||||
LICENSE
|
||||
MANIFEST.in
|
||||
Makefile
|
||||
README.rst
|
||||
setup.cfg
|
||||
setup.py
|
||||
tox.ini
|
||||
Flask.egg-info/PKG-INFO
|
||||
Flask.egg-info/SOURCES.txt
|
||||
Flask.egg-info/dependency_links.txt
|
||||
Flask.egg-info/entry_points.txt
|
||||
Flask.egg-info/not-zip-safe
|
||||
Flask.egg-info/requires.txt
|
||||
Flask.egg-info/top_level.txt
|
||||
artwork/LICENSE
|
||||
artwork/logo-full.svg
|
||||
artwork/logo-lineart.svg
|
||||
docs/Makefile
|
||||
docs/advanced_foreword.rst
|
||||
docs/api.rst
|
||||
docs/appcontext.rst
|
||||
docs/becomingbig.rst
|
||||
docs/blueprints.rst
|
||||
docs/changelog.rst
|
||||
docs/cli.rst
|
||||
docs/conf.py
|
||||
docs/config.rst
|
||||
docs/contents.rst.inc
|
||||
docs/contributing.rst
|
||||
docs/design.rst
|
||||
docs/errorhandling.rst
|
||||
docs/extensiondev.rst
|
||||
docs/extensions.rst
|
||||
docs/flaskstyle.sty
|
||||
docs/foreword.rst
|
||||
docs/htmlfaq.rst
|
||||
docs/index.rst
|
||||
docs/installation.rst
|
||||
docs/latexindex.rst
|
||||
docs/license.rst
|
||||
docs/logging.rst
|
||||
docs/logo.pdf
|
||||
docs/make.bat
|
||||
docs/quickstart.rst
|
||||
docs/reqcontext.rst
|
||||
docs/security.rst
|
||||
docs/server.rst
|
||||
docs/shell.rst
|
||||
docs/signals.rst
|
||||
docs/styleguide.rst
|
||||
docs/templating.rst
|
||||
docs/testing.rst
|
||||
docs/unicode.rst
|
||||
docs/upgrading.rst
|
||||
docs/views.rst
|
||||
docs/_static/debugger.png
|
||||
docs/_static/flask-favicon.ico
|
||||
docs/_static/flask.png
|
||||
docs/_static/logo-full.png
|
||||
docs/_static/no.png
|
||||
docs/_static/pycharm-runconfig.png
|
||||
docs/_static/touch-icon.png
|
||||
docs/_static/yes.png
|
||||
docs/deploying/cgi.rst
|
||||
docs/deploying/fastcgi.rst
|
||||
docs/deploying/index.rst
|
||||
docs/deploying/mod_wsgi.rst
|
||||
docs/deploying/uwsgi.rst
|
||||
docs/deploying/wsgi-standalone.rst
|
||||
docs/patterns/apierrors.rst
|
||||
docs/patterns/appdispatch.rst
|
||||
docs/patterns/appfactories.rst
|
||||
docs/patterns/caching.rst
|
||||
docs/patterns/celery.rst
|
||||
docs/patterns/deferredcallbacks.rst
|
||||
docs/patterns/distribute.rst
|
||||
docs/patterns/errorpages.rst
|
||||
docs/patterns/fabric.rst
|
||||
docs/patterns/favicon.rst
|
||||
docs/patterns/fileuploads.rst
|
||||
docs/patterns/flashing.rst
|
||||
docs/patterns/index.rst
|
||||
docs/patterns/jquery.rst
|
||||
docs/patterns/lazyloading.rst
|
||||
docs/patterns/methodoverrides.rst
|
||||
docs/patterns/mongokit.rst
|
||||
docs/patterns/packages.rst
|
||||
docs/patterns/requestchecksum.rst
|
||||
docs/patterns/sqlalchemy.rst
|
||||
docs/patterns/sqlite3.rst
|
||||
docs/patterns/streaming.rst
|
||||
docs/patterns/subclassing.rst
|
||||
docs/patterns/templateinheritance.rst
|
||||
docs/patterns/urlprocessors.rst
|
||||
docs/patterns/viewdecorators.rst
|
||||
docs/patterns/wtforms.rst
|
||||
docs/tutorial/blog.rst
|
||||
docs/tutorial/database.rst
|
||||
docs/tutorial/deploy.rst
|
||||
docs/tutorial/factory.rst
|
||||
docs/tutorial/flaskr_edit.png
|
||||
docs/tutorial/flaskr_index.png
|
||||
docs/tutorial/flaskr_login.png
|
||||
docs/tutorial/index.rst
|
||||
docs/tutorial/install.rst
|
||||
docs/tutorial/layout.rst
|
||||
docs/tutorial/next.rst
|
||||
docs/tutorial/static.rst
|
||||
docs/tutorial/templates.rst
|
||||
docs/tutorial/tests.rst
|
||||
docs/tutorial/views.rst
|
||||
examples/javascript/.gitignore
|
||||
examples/javascript/LICENSE
|
||||
examples/javascript/MANIFEST.in
|
||||
examples/javascript/README.rst
|
||||
examples/javascript/setup.cfg
|
||||
examples/javascript/setup.py
|
||||
examples/javascript/js_example/__init__.py
|
||||
examples/javascript/js_example/views.py
|
||||
examples/javascript/js_example/templates/base.html
|
||||
examples/javascript/js_example/templates/fetch.html
|
||||
examples/javascript/js_example/templates/jquery.html
|
||||
examples/javascript/js_example/templates/plain.html
|
||||
examples/javascript/tests/conftest.py
|
||||
examples/javascript/tests/test_js_example.py
|
||||
examples/tutorial/.gitignore
|
||||
examples/tutorial/LICENSE
|
||||
examples/tutorial/MANIFEST.in
|
||||
examples/tutorial/README.rst
|
||||
examples/tutorial/setup.cfg
|
||||
examples/tutorial/setup.py
|
||||
examples/tutorial/flaskr/__init__.py
|
||||
examples/tutorial/flaskr/auth.py
|
||||
examples/tutorial/flaskr/blog.py
|
||||
examples/tutorial/flaskr/db.py
|
||||
examples/tutorial/flaskr/schema.sql
|
||||
examples/tutorial/flaskr/static/style.css
|
||||
examples/tutorial/flaskr/templates/base.html
|
||||
examples/tutorial/flaskr/templates/auth/login.html
|
||||
examples/tutorial/flaskr/templates/auth/register.html
|
||||
examples/tutorial/flaskr/templates/blog/create.html
|
||||
examples/tutorial/flaskr/templates/blog/index.html
|
||||
examples/tutorial/flaskr/templates/blog/update.html
|
||||
examples/tutorial/tests/conftest.py
|
||||
examples/tutorial/tests/data.sql
|
||||
examples/tutorial/tests/test_auth.py
|
||||
examples/tutorial/tests/test_blog.py
|
||||
examples/tutorial/tests/test_db.py
|
||||
examples/tutorial/tests/test_factory.py
|
||||
flask/__init__.py
|
||||
flask/__main__.py
|
||||
flask/_compat.py
|
||||
flask/app.py
|
||||
flask/blueprints.py
|
||||
flask/cli.py
|
||||
flask/config.py
|
||||
flask/ctx.py
|
||||
flask/debughelpers.py
|
||||
flask/globals.py
|
||||
flask/helpers.py
|
||||
flask/logging.py
|
||||
flask/sessions.py
|
||||
flask/signals.py
|
||||
flask/templating.py
|
||||
flask/testing.py
|
||||
flask/views.py
|
||||
flask/wrappers.py
|
||||
flask/json/__init__.py
|
||||
flask/json/tag.py
|
||||
tests/conftest.py
|
||||
tests/test_appctx.py
|
||||
tests/test_basic.py
|
||||
tests/test_blueprints.py
|
||||
tests/test_cli.py
|
||||
tests/test_config.py
|
||||
tests/test_helpers.py
|
||||
tests/test_instance_config.py
|
||||
tests/test_json_tag.py
|
||||
tests/test_logging.py
|
||||
tests/test_regression.py
|
||||
tests/test_reqctx.py
|
||||
tests/test_signals.py
|
||||
tests/test_subclassing.py
|
||||
tests/test_templating.py
|
||||
tests/test_testing.py
|
||||
tests/test_user_error_handler.py
|
||||
tests/test_views.py
|
||||
tests/static/config.json
|
||||
tests/static/index.html
|
||||
tests/templates/_macro.html
|
||||
tests/templates/context_template.html
|
||||
tests/templates/escaping_template.html
|
||||
tests/templates/mail.txt
|
||||
tests/templates/non_escaping_template.txt
|
||||
tests/templates/simple_template.html
|
||||
tests/templates/template_filter.html
|
||||
tests/templates/template_test.html
|
||||
tests/templates/nested/nested.txt
|
||||
tests/test_apps/.env
|
||||
tests/test_apps/.flaskenv
|
||||
tests/test_apps/blueprintapp/__init__.py
|
||||
tests/test_apps/blueprintapp/apps/__init__.py
|
||||
tests/test_apps/blueprintapp/apps/admin/__init__.py
|
||||
tests/test_apps/blueprintapp/apps/admin/static/test.txt
|
||||
tests/test_apps/blueprintapp/apps/admin/static/css/test.css
|
||||
tests/test_apps/blueprintapp/apps/admin/templates/admin/index.html
|
||||
tests/test_apps/blueprintapp/apps/frontend/__init__.py
|
||||
tests/test_apps/blueprintapp/apps/frontend/templates/frontend/index.html
|
||||
tests/test_apps/cliapp/__init__.py
|
||||
tests/test_apps/cliapp/app.py
|
||||
tests/test_apps/cliapp/factory.py
|
||||
tests/test_apps/cliapp/importerrorapp.py
|
||||
tests/test_apps/cliapp/message.txt
|
||||
tests/test_apps/cliapp/multiapp.py
|
||||
tests/test_apps/cliapp/inner1/__init__.py
|
||||
tests/test_apps/cliapp/inner1/inner2/__init__.py
|
||||
tests/test_apps/cliapp/inner1/inner2/flask.py
|
||||
tests/test_apps/helloworld/hello.py
|
||||
tests/test_apps/helloworld/wsgi.py
|
||||
tests/test_apps/subdomaintestmodule/__init__.py
|
||||
tests/test_apps/subdomaintestmodule/static/hello.txt
|
||||
@@ -1,3 +0,0 @@
|
||||
[console_scripts]
|
||||
flask = flask.cli:main
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
../flask/testing.py
|
||||
../flask/templating.py
|
||||
../flask/__main__.py
|
||||
../flask/sessions.py
|
||||
../flask/signals.py
|
||||
../flask/helpers.py
|
||||
../flask/debughelpers.py
|
||||
../flask/wrappers.py
|
||||
../flask/app.py
|
||||
../flask/ctx.py
|
||||
../flask/config.py
|
||||
../flask/logging.py
|
||||
../flask/blueprints.py
|
||||
../flask/views.py
|
||||
../flask/cli.py
|
||||
../flask/_compat.py
|
||||
../flask/globals.py
|
||||
../flask/__init__.py
|
||||
../flask/json/tag.py
|
||||
../flask/json/__init__.py
|
||||
../flask/testing.pyc
|
||||
../flask/templating.pyc
|
||||
../flask/__main__.pyc
|
||||
../flask/sessions.pyc
|
||||
../flask/signals.pyc
|
||||
../flask/helpers.pyc
|
||||
../flask/debughelpers.pyc
|
||||
../flask/wrappers.pyc
|
||||
../flask/app.pyc
|
||||
../flask/ctx.pyc
|
||||
../flask/config.pyc
|
||||
../flask/logging.pyc
|
||||
../flask/blueprints.pyc
|
||||
../flask/views.pyc
|
||||
../flask/cli.pyc
|
||||
../flask/_compat.pyc
|
||||
../flask/globals.pyc
|
||||
../flask/__init__.pyc
|
||||
../flask/json/tag.pyc
|
||||
../flask/json/__init__.pyc
|
||||
not-zip-safe
|
||||
entry_points.txt
|
||||
dependency_links.txt
|
||||
PKG-INFO
|
||||
top_level.txt
|
||||
requires.txt
|
||||
SOURCES.txt
|
||||
../../../../bin/flask
|
||||
@@ -1,20 +0,0 @@
|
||||
Werkzeug>=0.14
|
||||
Jinja2>=2.10
|
||||
itsdangerous>=0.24
|
||||
click>=5.1
|
||||
|
||||
[dev]
|
||||
pytest>=3
|
||||
coverage
|
||||
tox
|
||||
sphinx
|
||||
pallets-sphinx-themes
|
||||
sphinxcontrib-log-cabinet
|
||||
|
||||
[docs]
|
||||
sphinx
|
||||
pallets-sphinx-themes
|
||||
sphinxcontrib-log-cabinet
|
||||
|
||||
[dotenv]
|
||||
python-dotenv
|
||||
@@ -1 +0,0 @@
|
||||
flask
|
||||
@@ -1,62 +0,0 @@
|
||||
Metadata-Version: 1.1
|
||||
Name: Jinja2
|
||||
Version: 2.10
|
||||
Summary: A small but fast and easy to use stand-alone template engine written in pure python.
|
||||
Home-page: http://jinja.pocoo.org/
|
||||
Author: Armin Ronacher
|
||||
Author-email: armin.ronacher@active-4.com
|
||||
License: BSD
|
||||
Description:
|
||||
Jinja2
|
||||
~~~~~~
|
||||
|
||||
Jinja2 is a template engine written in pure Python. It provides a
|
||||
`Django`_ inspired non-XML syntax but supports inline expressions and
|
||||
an optional `sandboxed`_ environment.
|
||||
|
||||
Nutshell
|
||||
--------
|
||||
|
||||
Here a small example of a Jinja template::
|
||||
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Memberlist{% endblock %}
|
||||
{% block content %}
|
||||
<ul>
|
||||
{% for user in users %}
|
||||
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
Philosophy
|
||||
----------
|
||||
|
||||
Application logic is for the controller but don't try to make the life
|
||||
for the template designer too hard by giving him too few functionality.
|
||||
|
||||
For more informations visit the new `Jinja2 webpage`_ and `documentation`_.
|
||||
|
||||
.. _sandboxed: https://en.wikipedia.org/wiki/Sandbox_(computer_security)
|
||||
.. _Django: https://www.djangoproject.com/
|
||||
.. _Jinja2 webpage: http://jinja.pocoo.org/
|
||||
.. _documentation: http://jinja.pocoo.org/2/documentation/
|
||||
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Environment :: Web Environment
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.6
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
Classifier: Topic :: Text Processing :: Markup :: HTML
|
||||
@@ -1,133 +0,0 @@
|
||||
AUTHORS
|
||||
CHANGES.rst
|
||||
LICENSE
|
||||
MANIFEST.in
|
||||
README.rst
|
||||
setup.cfg
|
||||
setup.py
|
||||
Jinja2.egg-info/PKG-INFO
|
||||
Jinja2.egg-info/SOURCES.txt
|
||||
Jinja2.egg-info/dependency_links.txt
|
||||
Jinja2.egg-info/entry_points.txt
|
||||
Jinja2.egg-info/not-zip-safe
|
||||
Jinja2.egg-info/requires.txt
|
||||
Jinja2.egg-info/top_level.txt
|
||||
artwork/jinjalogo.svg
|
||||
docs/Makefile
|
||||
docs/api.rst
|
||||
docs/cache_extension.py
|
||||
docs/changelog.rst
|
||||
docs/conf.py
|
||||
docs/contents.rst.inc
|
||||
docs/extensions.rst
|
||||
docs/faq.rst
|
||||
docs/index.rst
|
||||
docs/integration.rst
|
||||
docs/intro.rst
|
||||
docs/jinjaext.py
|
||||
docs/jinjastyle.sty
|
||||
docs/latexindex.rst
|
||||
docs/logo.pdf
|
||||
docs/nativetypes.rst
|
||||
docs/sandbox.rst
|
||||
docs/switching.rst
|
||||
docs/templates.rst
|
||||
docs/tricks.rst
|
||||
docs/_static/.ignore
|
||||
docs/_static/jinja-small.png
|
||||
docs/_templates/sidebarintro.html
|
||||
docs/_templates/sidebarlogo.html
|
||||
docs/_themes/LICENSE
|
||||
docs/_themes/README
|
||||
docs/_themes/jinja/layout.html
|
||||
docs/_themes/jinja/relations.html
|
||||
docs/_themes/jinja/theme.conf
|
||||
docs/_themes/jinja/static/jinja.css_t
|
||||
examples/bench.py
|
||||
examples/profile.py
|
||||
examples/basic/cycle.py
|
||||
examples/basic/debugger.py
|
||||
examples/basic/inheritance.py
|
||||
examples/basic/test.py
|
||||
examples/basic/test_filter_and_linestatements.py
|
||||
examples/basic/test_loop_filter.py
|
||||
examples/basic/translate.py
|
||||
examples/basic/templates/broken.html
|
||||
examples/basic/templates/subbroken.html
|
||||
examples/rwbench/djangoext.py
|
||||
examples/rwbench/rwbench.py
|
||||
examples/rwbench/django/_form.html
|
||||
examples/rwbench/django/_input_field.html
|
||||
examples/rwbench/django/_textarea.html
|
||||
examples/rwbench/django/index.html
|
||||
examples/rwbench/django/layout.html
|
||||
examples/rwbench/genshi/helpers.html
|
||||
examples/rwbench/genshi/index.html
|
||||
examples/rwbench/genshi/layout.html
|
||||
examples/rwbench/jinja/helpers.html
|
||||
examples/rwbench/jinja/index.html
|
||||
examples/rwbench/jinja/layout.html
|
||||
examples/rwbench/mako/helpers.html
|
||||
examples/rwbench/mako/index.html
|
||||
examples/rwbench/mako/layout.html
|
||||
ext/djangojinja2.py
|
||||
ext/inlinegettext.py
|
||||
ext/jinja.el
|
||||
ext/Vim/jinja.vim
|
||||
ext/django2jinja/django2jinja.py
|
||||
ext/django2jinja/example.py
|
||||
ext/django2jinja/templates/index.html
|
||||
ext/django2jinja/templates/layout.html
|
||||
ext/django2jinja/templates/subtemplate.html
|
||||
jinja2/__init__.py
|
||||
jinja2/_compat.py
|
||||
jinja2/_identifier.py
|
||||
jinja2/asyncfilters.py
|
||||
jinja2/asyncsupport.py
|
||||
jinja2/bccache.py
|
||||
jinja2/compiler.py
|
||||
jinja2/constants.py
|
||||
jinja2/debug.py
|
||||
jinja2/defaults.py
|
||||
jinja2/environment.py
|
||||
jinja2/exceptions.py
|
||||
jinja2/ext.py
|
||||
jinja2/filters.py
|
||||
jinja2/idtracking.py
|
||||
jinja2/lexer.py
|
||||
jinja2/loaders.py
|
||||
jinja2/meta.py
|
||||
jinja2/nativetypes.py
|
||||
jinja2/nodes.py
|
||||
jinja2/optimizer.py
|
||||
jinja2/parser.py
|
||||
jinja2/runtime.py
|
||||
jinja2/sandbox.py
|
||||
jinja2/tests.py
|
||||
jinja2/utils.py
|
||||
jinja2/visitor.py
|
||||
tests/conftest.py
|
||||
tests/test_api.py
|
||||
tests/test_async.py
|
||||
tests/test_asyncfilters.py
|
||||
tests/test_bytecode_cache.py
|
||||
tests/test_core_tags.py
|
||||
tests/test_debug.py
|
||||
tests/test_ext.py
|
||||
tests/test_features.py
|
||||
tests/test_filters.py
|
||||
tests/test_idtracking.py
|
||||
tests/test_imports.py
|
||||
tests/test_inheritance.py
|
||||
tests/test_lexnparse.py
|
||||
tests/test_loader.py
|
||||
tests/test_nativetypes.py
|
||||
tests/test_regression.py
|
||||
tests/test_security.py
|
||||
tests/test_tests.py
|
||||
tests/test_utils.py
|
||||
tests/res/__init__.py
|
||||
tests/res/templates/broken.html
|
||||
tests/res/templates/syntaxerror.html
|
||||
tests/res/templates/test.html
|
||||
tests/res/templates/foo/test.html
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
|
||||
[babel.extractors]
|
||||
jinja2 = jinja2.ext:babel_extract[i18n]
|
||||
|
||||
@@ -1,61 +0,0 @@
|
||||
../jinja2/lexer.py
|
||||
../jinja2/idtracking.py
|
||||
../jinja2/_identifier.py
|
||||
../jinja2/nodes.py
|
||||
../jinja2/asyncfilters.py
|
||||
../jinja2/loaders.py
|
||||
../jinja2/defaults.py
|
||||
../jinja2/meta.py
|
||||
../jinja2/compiler.py
|
||||
../jinja2/environment.py
|
||||
../jinja2/tests.py
|
||||
../jinja2/sandbox.py
|
||||
../jinja2/filters.py
|
||||
../jinja2/exceptions.py
|
||||
../jinja2/asyncsupport.py
|
||||
../jinja2/visitor.py
|
||||
../jinja2/constants.py
|
||||
../jinja2/utils.py
|
||||
../jinja2/ext.py
|
||||
../jinja2/optimizer.py
|
||||
../jinja2/nativetypes.py
|
||||
../jinja2/parser.py
|
||||
../jinja2/runtime.py
|
||||
../jinja2/debug.py
|
||||
../jinja2/_compat.py
|
||||
../jinja2/bccache.py
|
||||
../jinja2/__init__.py
|
||||
../jinja2/lexer.pyc
|
||||
../jinja2/idtracking.pyc
|
||||
../jinja2/_identifier.pyc
|
||||
../jinja2/nodes.pyc
|
||||
../jinja2/asyncfilters.pyc
|
||||
../jinja2/loaders.pyc
|
||||
../jinja2/defaults.pyc
|
||||
../jinja2/meta.pyc
|
||||
../jinja2/compiler.pyc
|
||||
../jinja2/environment.pyc
|
||||
../jinja2/tests.pyc
|
||||
../jinja2/sandbox.pyc
|
||||
../jinja2/filters.pyc
|
||||
../jinja2/exceptions.pyc
|
||||
../jinja2/asyncsupport.pyc
|
||||
../jinja2/visitor.pyc
|
||||
../jinja2/constants.pyc
|
||||
../jinja2/utils.pyc
|
||||
../jinja2/ext.pyc
|
||||
../jinja2/optimizer.pyc
|
||||
../jinja2/nativetypes.pyc
|
||||
../jinja2/parser.pyc
|
||||
../jinja2/runtime.pyc
|
||||
../jinja2/debug.pyc
|
||||
../jinja2/_compat.pyc
|
||||
../jinja2/bccache.pyc
|
||||
../jinja2/__init__.pyc
|
||||
not-zip-safe
|
||||
entry_points.txt
|
||||
dependency_links.txt
|
||||
PKG-INFO
|
||||
top_level.txt
|
||||
requires.txt
|
||||
SOURCES.txt
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
MarkupSafe>=0.23
|
||||
|
||||
[i18n]
|
||||
Babel>=0.8
|
||||
@@ -1 +0,0 @@
|
||||
jinja2
|
||||
@@ -1,36 +0,0 @@
|
||||
Jinja2
|
||||
~~~~~~
|
||||
|
||||
Jinja2 is a template engine written in pure Python. It provides a
|
||||
`Django`_ inspired non-XML syntax but supports inline expressions and
|
||||
an optional `sandboxed`_ environment.
|
||||
|
||||
Nutshell
|
||||
--------
|
||||
|
||||
Here a small example of a Jinja template::
|
||||
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Memberlist{% endblock %}
|
||||
{% block content %}
|
||||
<ul>
|
||||
{% for user in users %}
|
||||
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
Philosophy
|
||||
----------
|
||||
|
||||
Application logic is for the controller but don't try to make the life
|
||||
for the template designer too hard by giving him too few functionality.
|
||||
|
||||
For more informations visit the new `Jinja2 webpage`_ and `documentation`_.
|
||||
|
||||
.. _sandboxed: http://en.wikipedia.org/wiki/Sandbox_(computer_security)
|
||||
.. _Django: http://www.djangoproject.com/
|
||||
.. _Jinja2 webpage: http://jinja.pocoo.org/
|
||||
.. _documentation: http://jinja.pocoo.org/2/documentation/
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
pip
|
||||
@@ -1,65 +0,0 @@
|
||||
Metadata-Version: 2.0
|
||||
Name: Jinja2
|
||||
Version: 2.9.6
|
||||
Summary: A small but fast and easy to use stand-alone template engine written in pure python.
|
||||
Home-page: http://jinja.pocoo.org/
|
||||
Author: Armin Ronacher
|
||||
Author-email: armin.ronacher@active-4.com
|
||||
License: BSD
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Environment :: Web Environment
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.6
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
Classifier: Topic :: Text Processing :: Markup :: HTML
|
||||
Requires-Dist: MarkupSafe (>=0.23)
|
||||
Provides-Extra: i18n
|
||||
Requires-Dist: Babel (>=0.8); extra == 'i18n'
|
||||
|
||||
Jinja2
|
||||
~~~~~~
|
||||
|
||||
Jinja2 is a template engine written in pure Python. It provides a
|
||||
`Django`_ inspired non-XML syntax but supports inline expressions and
|
||||
an optional `sandboxed`_ environment.
|
||||
|
||||
Nutshell
|
||||
--------
|
||||
|
||||
Here a small example of a Jinja template::
|
||||
|
||||
{% extends 'base.html' %}
|
||||
{% block title %}Memberlist{% endblock %}
|
||||
{% block content %}
|
||||
<ul>
|
||||
{% for user in users %}
|
||||
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock %}
|
||||
|
||||
Philosophy
|
||||
----------
|
||||
|
||||
Application logic is for the controller but don't try to make the life
|
||||
for the template designer too hard by giving him too few functionality.
|
||||
|
||||
For more informations visit the new `Jinja2 webpage`_ and `documentation`_.
|
||||
|
||||
.. _sandboxed: http://en.wikipedia.org/wiki/Sandbox_(computer_security)
|
||||
.. _Django: http://www.djangoproject.com/
|
||||
.. _Jinja2 webpage: http://jinja.pocoo.org/
|
||||
.. _documentation: http://jinja.pocoo.org/2/documentation/
|
||||
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
jinja2/__init__.py,sha256=Cx_UnJO4i_GqvKQsOu__mvGE_eMJSsBqITa26irtg5A,2565
|
||||
jinja2/_compat.py,sha256=xP60CE5Qr8FTYcDE1f54tbZLKGvMwYml4-8T7Q4KG9k,2596
|
||||
jinja2/_stringdefs.py,sha256=PYtqTmmWIhjXlFBoH-eE6fJkQvlu7nxUyQ2YlFB97VA,589381
|
||||
jinja2/asyncfilters.py,sha256=cTDPvrS8Hp_IkwsZ1m9af_lr5nHysw7uTa5gV0NmZVE,4144
|
||||
jinja2/asyncsupport.py,sha256=ZJO1Fdd9R93sDLrk6TZNuMQGgtuDmpTlENNRkLwZF7c,7765
|
||||
jinja2/bccache.py,sha256=0xoVw0R9nj3vtzPl9g-zB5BKTLFJ7FFMq2ABbn1IkCI,12793
|
||||
jinja2/compiler.py,sha256=lE5owyPwT1cGGZxWyzQtZLW7Uj1g3Vw1oVtBU8Uc_uM,62929
|
||||
jinja2/constants.py,sha256=uwwV8ZUhHhacAuz5PTwckfsbqBaqM7aKfyJL7kGX5YQ,1626
|
||||
jinja2/debug.py,sha256=UqEbKb4zofBABwvyA77Kr0-5IAQawKqC9t8ZeTIzpGU,12038
|
||||
jinja2/defaults.py,sha256=GvVEQqIRvRMCbQF2NZSr0mlEN8lxvGixU5wIIAeRe1A,1323
|
||||
jinja2/environment.py,sha256=z91L_efdYs-KNs6DBxQWDyYncOwOqn_0J4M5CfFj0Q8,50848
|
||||
jinja2/exceptions.py,sha256=_Rj-NVi98Q6AiEjYQOsP8dEIdu5AlmRHzcSNOPdWix4,4428
|
||||
jinja2/ext.py,sha256=9xq8fd_QPBIe4Z7hE1XawB7f1EDHrVZjpb2JiRTiG94,23867
|
||||
jinja2/filters.py,sha256=1OYGhyN84yVmFUIOwJNRV_StqTCfPhnRLfJTmWbEe_8,33424
|
||||
jinja2/idtracking.py,sha256=HHcCOMsQhCrrjwYAmikKqq_XetXLovCjXAThh9WbRAc,8760
|
||||
jinja2/lexer.py,sha256=W4A830e-fj12zRT6rL7H91F4D6xwED5LjR8iMxjWuVQ,28238
|
||||
jinja2/loaders.py,sha256=xiTuURKAEObyym0nU8PCIXu_Qp8fn0AJ5oIADUUm-5Q,17382
|
||||
jinja2/meta.py,sha256=fmKHxkmZYAOm9QyWWy8EMd6eefAIh234rkBMW2X4ZR8,4340
|
||||
jinja2/nodes.py,sha256=4_Ucxbkohtj4BAlpV0w_MpVmIxJNaVXDTBb4EHBA2JI,29392
|
||||
jinja2/optimizer.py,sha256=MsdlFACJ0FRdPtjmCAdt7JQ9SGrXFaDNUaslsWQaG3M,1722
|
||||
jinja2/parser.py,sha256=3tc82qO1Ovs9och_PjirbAmnWNT77n4wWjIQ8pEVKvU,35465
|
||||
jinja2/runtime.py,sha256=axkTQXg2-oc_Cm35NEMDDas3Jbq3ATxNrDOEa5v3wIw,26835
|
||||
jinja2/sandbox.py,sha256=Jx4MTxly8KvdkSWyui_kHY1_ZZ0RAQL4ojAy1KDRyK0,16707
|
||||
jinja2/tests.py,sha256=iFuUTbUYv7TFffq2aTswCRdIhQ6wyrby1YevChVPqkE,4428
|
||||
jinja2/utils.py,sha256=BIFqeXXsCUSjWx6MEwYhY6V4tXzVNs9WRXfB60MA9HY,19941
|
||||
jinja2/visitor.py,sha256=JD1H1cANA29JcntFfN5fPyqQxB4bI4wC00BzZa-XHks,3316
|
||||
Jinja2-2.9.6.dist-info/DESCRIPTION.rst,sha256=CXIS1UnPSk5_lZBS6Lb8ko-3lqGfjsiUwNBLXCTj2lc,975
|
||||
Jinja2-2.9.6.dist-info/entry_points.txt,sha256=NdzVcOrqyNyKDxD09aERj__3bFx2paZhizFDsKmVhiA,72
|
||||
Jinja2-2.9.6.dist-info/LICENSE.txt,sha256=JvzUNv3Io51EiWrAPm8d_SXjhJnEjyDYvB3Tvwqqils,1554
|
||||
Jinja2-2.9.6.dist-info/METADATA,sha256=53LSXlqC86JTyLSPsDyAOmyV4pXIzzmmZoUXz7ogytA,2172
|
||||
Jinja2-2.9.6.dist-info/metadata.json,sha256=vzvX25T4hwMOe1EIOBo9rpfiZerOB_KVLcplGG_qYtE,1394
|
||||
Jinja2-2.9.6.dist-info/RECORD,,
|
||||
Jinja2-2.9.6.dist-info/top_level.txt,sha256=PkeVWtLb3-CqjWi1fO29OCbj55EhX_chhKrCdrVe_zs,7
|
||||
Jinja2-2.9.6.dist-info/WHEEL,sha256=AvR0WeTpDaxT645bl5FQxUK6NPsTls2ttpcGJg3j1Xg,110
|
||||
Jinja2-2.9.6.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
jinja2/_compat.pyc,,
|
||||
jinja2/sandbox.pyc,,
|
||||
jinja2/_stringdefs.pyc,,
|
||||
jinja2/bccache.pyc,,
|
||||
jinja2/runtime.pyc,,
|
||||
jinja2/utils.pyc,,
|
||||
jinja2/parser.pyc,,
|
||||
jinja2/debug.pyc,,
|
||||
jinja2/lexer.pyc,,
|
||||
jinja2/defaults.pyc,,
|
||||
jinja2/visitor.pyc,,
|
||||
jinja2/nodes.pyc,,
|
||||
jinja2/environment.pyc,,
|
||||
jinja2/compiler.pyc,,
|
||||
jinja2/exceptions.pyc,,
|
||||
jinja2/filters.pyc,,
|
||||
jinja2/__init__.pyc,,
|
||||
jinja2/meta.pyc,,
|
||||
jinja2/loaders.pyc,,
|
||||
jinja2/ext.pyc,,
|
||||
jinja2/optimizer.pyc,,
|
||||
jinja2/constants.pyc,,
|
||||
jinja2/tests.pyc,,
|
||||
jinja2/idtracking.pyc,,
|
||||
@@ -1,6 +0,0 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.24.0)
|
||||
Root-Is-Purelib: true
|
||||
Tag: py2-none-any
|
||||
Tag: py3-none-any
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
|
||||
[babel.extractors]
|
||||
jinja2 = jinja2.ext:babel_extract[i18n]
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
{"license": "BSD", "name": "Jinja2", "metadata_version": "2.0", "generator": "bdist_wheel (0.24.0)", "summary": "A small but fast and easy to use stand-alone template engine written in pure python.", "run_requires": [{"requires": ["Babel (>=0.8)"], "extra": "i18n"}, {"requires": ["MarkupSafe (>=0.23)"]}], "version": "2.9.6", "extensions": {"python.details": {"project_urls": {"Home": "http://jinja.pocoo.org/"}, "document_names": {"description": "DESCRIPTION.rst", "license": "LICENSE.txt"}, "contacts": [{"role": "author", "email": "armin.ronacher@active-4.com", "name": "Armin Ronacher"}]}, "python.exports": {"babel.extractors": {"jinja2": "jinja2.ext:babel_extract [i18n]"}}}, "classifiers": ["Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Topic :: Internet :: WWW/HTTP :: Dynamic Content", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Text Processing :: Markup :: HTML"], "extras": ["i18n"]}
|
||||
@@ -1 +0,0 @@
|
||||
jinja2
|
||||
7
LICENSE
Normal file
7
LICENSE
Normal file
@@ -0,0 +1,7 @@
|
||||
Copyright (c) 2018, Comma.ai, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
9
Makefile
Normal file
9
Makefile
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
code_dir := $(shell pwd)
|
||||
|
||||
# TODO: Add a global build system
|
||||
|
||||
.PHONY: all
|
||||
all:
|
||||
cd selfdrive && PYTHONPATH=$(code_dir) PREPAREONLY=1 ./manager.py
|
||||
|
||||
@@ -1,133 +0,0 @@
|
||||
Metadata-Version: 1.1
|
||||
Name: MarkupSafe
|
||||
Version: 1.0
|
||||
Summary: Implements a XML/HTML/XHTML Markup safe string for Python
|
||||
Home-page: http://github.com/pallets/markupsafe
|
||||
Author: Armin Ronacher
|
||||
Author-email: armin.ronacher@active-4.com
|
||||
License: BSD
|
||||
Description: MarkupSafe
|
||||
==========
|
||||
|
||||
Implements a unicode subclass that supports HTML strings:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> from markupsafe import Markup, escape
|
||||
>>> escape("<script>alert(document.cookie);</script>")
|
||||
Markup(u'<script>alert(document.cookie);</script>')
|
||||
>>> tmpl = Markup("<em>%s</em>")
|
||||
>>> tmpl % "Peter > Lustig"
|
||||
Markup(u'<em>Peter > Lustig</em>')
|
||||
|
||||
If you want to make an object unicode that is not yet unicode
|
||||
but don't want to lose the taint information, you can use the
|
||||
``soft_unicode`` function. (On Python 3 you can also use ``soft_str`` which
|
||||
is a different name for the same function).
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> from markupsafe import soft_unicode
|
||||
>>> soft_unicode(42)
|
||||
u'42'
|
||||
>>> soft_unicode(Markup('foo'))
|
||||
Markup(u'foo')
|
||||
|
||||
HTML Representations
|
||||
--------------------
|
||||
|
||||
Objects can customize their HTML markup equivalent by overriding
|
||||
the ``__html__`` function:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> class Foo(object):
|
||||
... def __html__(self):
|
||||
... return '<strong>Nice</strong>'
|
||||
...
|
||||
>>> escape(Foo())
|
||||
Markup(u'<strong>Nice</strong>')
|
||||
>>> Markup(Foo())
|
||||
Markup(u'<strong>Nice</strong>')
|
||||
|
||||
Silent Escapes
|
||||
--------------
|
||||
|
||||
Since MarkupSafe 0.10 there is now also a separate escape function
|
||||
called ``escape_silent`` that returns an empty string for ``None`` for
|
||||
consistency with other systems that return empty strings for ``None``
|
||||
when escaping (for instance Pylons' webhelpers).
|
||||
|
||||
If you also want to use this for the escape method of the Markup
|
||||
object, you can create your own subclass that does that:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from markupsafe import Markup, escape_silent as escape
|
||||
|
||||
class SilentMarkup(Markup):
|
||||
__slots__ = ()
|
||||
|
||||
@classmethod
|
||||
def escape(cls, s):
|
||||
return cls(escape(s))
|
||||
|
||||
New-Style String Formatting
|
||||
---------------------------
|
||||
|
||||
Starting with MarkupSafe 0.21 new style string formats from Python 2.6 and
|
||||
3.x are now fully supported. Previously the escape behavior of those
|
||||
functions was spotty at best. The new implementations operates under the
|
||||
following algorithm:
|
||||
|
||||
1. if an object has an ``__html_format__`` method it is called as
|
||||
replacement for ``__format__`` with the format specifier. It either
|
||||
has to return a string or markup object.
|
||||
2. if an object has an ``__html__`` method it is called.
|
||||
3. otherwise the default format system of Python kicks in and the result
|
||||
is HTML escaped.
|
||||
|
||||
Here is how you can implement your own formatting:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class User(object):
|
||||
|
||||
def __init__(self, id, username):
|
||||
self.id = id
|
||||
self.username = username
|
||||
|
||||
def __html_format__(self, format_spec):
|
||||
if format_spec == 'link':
|
||||
return Markup('<a href="/user/{0}">{1}</a>').format(
|
||||
self.id,
|
||||
self.__html__(),
|
||||
)
|
||||
elif format_spec:
|
||||
raise ValueError('Invalid format spec')
|
||||
return self.__html__()
|
||||
|
||||
def __html__(self):
|
||||
return Markup('<span class=user>{0}</span>').format(self.username)
|
||||
|
||||
And to format that user:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
>>> user = User(1, 'foo')
|
||||
>>> Markup('<p>User: {0:link}').format(user)
|
||||
Markup(u'<p>User: <a href="/user/1"><span class=user>foo</span></a>')
|
||||
|
||||
Markupsafe supports Python 2.6, 2.7 and Python 3.3 and higher.
|
||||
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Environment :: Web Environment
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
Classifier: Topic :: Text Processing :: Markup :: HTML
|
||||
@@ -1,18 +0,0 @@
|
||||
AUTHORS
|
||||
CHANGES
|
||||
LICENSE
|
||||
MANIFEST.in
|
||||
README.rst
|
||||
setup.cfg
|
||||
setup.py
|
||||
tests.py
|
||||
MarkupSafe.egg-info/PKG-INFO
|
||||
MarkupSafe.egg-info/SOURCES.txt
|
||||
MarkupSafe.egg-info/dependency_links.txt
|
||||
MarkupSafe.egg-info/not-zip-safe
|
||||
MarkupSafe.egg-info/top_level.txt
|
||||
markupsafe/__init__.py
|
||||
markupsafe/_compat.py
|
||||
markupsafe/_constants.py
|
||||
markupsafe/_native.py
|
||||
markupsafe/_speedups.c
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
../markupsafe/__init__.py
|
||||
../markupsafe/_compat.py
|
||||
../markupsafe/_constants.py
|
||||
../markupsafe/_native.py
|
||||
../markupsafe/_speedups.c
|
||||
../markupsafe/__init__.pyc
|
||||
../markupsafe/_compat.pyc
|
||||
../markupsafe/_constants.pyc
|
||||
../markupsafe/_native.pyc
|
||||
../markupsafe/_speedups.so
|
||||
dependency_links.txt
|
||||
not-zip-safe
|
||||
PKG-INFO
|
||||
SOURCES.txt
|
||||
top_level.txt
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
markupsafe
|
||||
146
Pipfile
Normal file
146
Pipfile
Normal file
@@ -0,0 +1,146 @@
|
||||
[[source]]
|
||||
name = "pypi"
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
|
||||
[dev-packages]
|
||||
opencv-python= "==3.4.2.17"
|
||||
PyQt5 = "*"
|
||||
ipython = "*"
|
||||
networkx = "*"
|
||||
azure-common = "*"
|
||||
azure-nspkg = "*"
|
||||
azure-storage-blob = "*"
|
||||
azure-storage-common = "*"
|
||||
azure-storage-nspkg = "*"
|
||||
bincopy = "*"
|
||||
bleach = "*"
|
||||
boto = "*"
|
||||
"boto3" = "*"
|
||||
celery = "*"
|
||||
control = "*"
|
||||
datadog = "*"
|
||||
decorator = "*"
|
||||
dlib = "*"
|
||||
dominate = "*"
|
||||
elasticsearch = "*"
|
||||
fasteners = "*"
|
||||
future = "*"
|
||||
futures = "*"
|
||||
gevent = "*"
|
||||
pycocotools = {git = "https://github.com/cocodataset/cocoapi.git",subdirectory = "PythonAPI"}
|
||||
gunicorn = "*"
|
||||
"h5py" = "*"
|
||||
hexdump = "*"
|
||||
"html5lib" = "*"
|
||||
imageio = "*"
|
||||
intervaltree = "*"
|
||||
ipykernel = "*"
|
||||
joblib = "*"
|
||||
json-logging-py = "*"
|
||||
jupyter = "*"
|
||||
libarchive = "*"
|
||||
lru-dict = "*"
|
||||
lxml = "*"
|
||||
"mpld3" = "*"
|
||||
msgpack-python = "*"
|
||||
nbstripout = "*"
|
||||
nose-parameterized = "*"
|
||||
numpy = "*"
|
||||
osmium = "*"
|
||||
pbr = "*"
|
||||
percache = "*"
|
||||
pprofile = "*"
|
||||
psutil = "*"
|
||||
pycurl = "*"
|
||||
git-pylint-commit-hook = "*"
|
||||
pymongo = "*"
|
||||
"pynmea2" = "*"
|
||||
pypolyline = "*"
|
||||
pysendfile = "*"
|
||||
python-logstash = "*"
|
||||
pyvcd = "*"
|
||||
redis = "*"
|
||||
redlock = "*"
|
||||
"s2sphere" = "*"
|
||||
scikit-image = "*"
|
||||
"subprocess32" = "*"
|
||||
supervisor = "*"
|
||||
tenacity = "*"
|
||||
tensorflow-gpu = ""
|
||||
utm = "*"
|
||||
"v4l2" = "*"
|
||||
PyJWT = "==1.4.1"
|
||||
PyMySQL = "==0.9.2"
|
||||
Theano = "*"
|
||||
Werkzeug = "*"
|
||||
"backports.lzma" = "*"
|
||||
Flask-Cors = "*"
|
||||
Flask-SocketIO = "*"
|
||||
"GeoAlchemy2" = "*"
|
||||
Pygments = "*"
|
||||
PyNaCl = "*"
|
||||
"PySDL2" = "*"
|
||||
reverse_geocoder = "*"
|
||||
Shapely = "*"
|
||||
SQLAlchemy = "*"
|
||||
uWSGI = "*"
|
||||
scipy = "*"
|
||||
fastcluster = "*"
|
||||
backports-abc = "*"
|
||||
pygame = "*"
|
||||
simplejson = "*"
|
||||
python-logstash-async = "*"
|
||||
seaborn = "*"
|
||||
tensorflow-estimator = "*"
|
||||
pyproj = "*"
|
||||
mock = "*"
|
||||
blinker = "*"
|
||||
gast = "==0.2.2"
|
||||
matplotlib = "*"
|
||||
dictdiffer = "*"
|
||||
aenum = "*"
|
||||
coverage = "*"
|
||||
|
||||
[packages]
|
||||
overpy = {git = "https://github.com/commaai/python-overpy.git",ref = "f86529af402d4642e1faeb146671c40284007323"}
|
||||
atomicwrites = "*"
|
||||
cffi = "*"
|
||||
crcmod = "*"
|
||||
hexdump = "*"
|
||||
libusb1 = "*"
|
||||
numpy = "*"
|
||||
psutil = "*"
|
||||
pycapnp = "*"
|
||||
cryptography = "*"
|
||||
pyserial = "*"
|
||||
python-dateutil = "*"
|
||||
pyzmq = "*"
|
||||
raven = "*"
|
||||
requests = "*"
|
||||
setproctitle = "*"
|
||||
six = "*"
|
||||
smbus2 = "*"
|
||||
sympy = "*"
|
||||
tqdm = "*"
|
||||
Cython = "*"
|
||||
PyYAML = "*"
|
||||
websocket_client = "*"
|
||||
Logentries = {git = "https://github.com/commaai/le_python.git",ref = "feaeacb48f7f4bdb02c0a8fc092326d4e101b7f2"}
|
||||
urllib3 = "*"
|
||||
chardet = "*"
|
||||
idna = "*"
|
||||
gunicorn = "*"
|
||||
utm = "*"
|
||||
json-rpc = "*"
|
||||
Flask = "*"
|
||||
PyJWT = "*"
|
||||
"Jinja2" = "*"
|
||||
nose = "*"
|
||||
flake8 = "*"
|
||||
pylint = "*"
|
||||
pycryptodome = "*"
|
||||
pillow = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.7.3"
|
||||
2815
Pipfile.lock
generated
Normal file
2815
Pipfile.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
243
README.md
Normal file
243
README.md
Normal file
@@ -0,0 +1,243 @@
|
||||
[](#)
|
||||
|
||||
Welcome to openpilot
|
||||
======
|
||||
|
||||
[openpilot](http://github.com/commaai/openpilot) is an open source driver assistance system. Currently, it performs the functions of Adaptive Cruise Control (ACC) and Automated Lane Centering (ALC) for selected Honda, Toyota, Acura, Lexus, Chevrolet, Hyundai, Kia, Subaru, Volkswagen. It's about on par with Tesla Autopilot and GM Super Cruise, and better than [all other manufacturers](http://www.thedrive.com/tech/5707/the-war-for-autonomous-driving-part-iii-us-vs-germany-vs-japan).
|
||||
|
||||
The openpilot codebase has been written to be concise and to enable rapid prototyping. We look forward to your contributions - improving real vehicle automation has never been easier.
|
||||
|
||||
Table of Contents
|
||||
=======================
|
||||
|
||||
* [Community](#community)
|
||||
* [Hardware](#hardware)
|
||||
* [Supported Cars](#supported-cars)
|
||||
* [Community Maintained Cars](#community-maintained-cars)
|
||||
* [In Progress Cars](#in-progress-cars)
|
||||
* [How can I add support for my car?](#how-can-i-add-support-for-my-car)
|
||||
* [Directory structure](#directory-structure)
|
||||
* [User Data / chffr Account / Crash Reporting](#user-data--chffr-account--crash-reporting)
|
||||
* [Testing on PC](#testing-on-pc)
|
||||
* [Contributing](#contributing)
|
||||
* [Licensing](#licensing)
|
||||
|
||||
---
|
||||
|
||||
Community
|
||||
------
|
||||
|
||||
openpilot is developed by [comma.ai](https://comma.ai/) and users like you.
|
||||
|
||||
[Follow us on Twitter](https://twitter.com/comma_ai) and [join our Discord](https://discord.comma.ai).
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td><a href="https://www.youtube.com/watch?v=mgAbfr42oI8" title="YouTube" rel="noopener"><img src="https://i.imgur.com/kAtT6Ei.png"></a></td>
|
||||
<td><a href="https://www.youtube.com/watch?v=394rJKeh76k" title="YouTube" rel="noopener"><img src="https://i.imgur.com/lTt8cS2.png"></a></td>
|
||||
<td><a href="https://www.youtube.com/watch?v=1iNOc3cq8cs" title="YouTube" rel="noopener"><img src="https://i.imgur.com/ANnuSpe.png"></a></td>
|
||||
<td><a href="https://www.youtube.com/watch?v=Vr6NgrB-zHw" title="YouTube" rel="noopener"><img src="https://i.imgur.com/Qypanuq.png"></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://www.youtube.com/watch?v=Ug41KIKF0oo" title="YouTube" rel="noopener"><img src="https://i.imgur.com/3caZ7xM.png"></a></td>
|
||||
<td><a href="https://www.youtube.com/watch?v=NVR_CdG1FRg" title="YouTube" rel="noopener"><img src="https://i.imgur.com/bAZOwql.png"></a></td>
|
||||
<td><a href="https://www.youtube.com/watch?v=tkEvIdzdfUE" title="YouTube" rel="noopener"><img src="https://i.imgur.com/EFINEzG.png"></a></td>
|
||||
<td><a href="https://www.youtube.com/watch?v=_P-N1ewNne4" title="YouTube" rel="noopener"><img src="https://i.imgur.com/gAyAq22.png"></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Hardware
|
||||
------
|
||||
|
||||
At the moment openpilot supports the [EON DevKit](https://comma.ai/shop/products/eon-dashcam-devkit). A [car harness](https://comma.ai/shop/products/car-harness) is recommended to connect the EON to the car. We'd like to support other platforms as well.
|
||||
|
||||
Install openpilot on a neo device by entering ``https://openpilot.comma.ai`` during NEOS setup.
|
||||
|
||||
Supported Cars
|
||||
------
|
||||
|
||||
| Make | Model (US Market Reference) | Supported Package | Lateral | Longitudinal | No Accel Below | No Steer Below |
|
||||
| ----------------------| -----------------------------------| ---------------------| --------| ---------------| -----------------| ---------------|
|
||||
| Acura | ILX 2016-18 | AcuraWatch Plus | Yes | Yes | 25mph<sup>1</sup>| 25mph |
|
||||
| Acura | RDX 2016-18 | AcuraWatch Plus | Yes | Yes | 25mph<sup>1</sup>| 12mph |
|
||||
| Buick<sup>3</sup> | Regal 2018<sup>6</sup> | Adaptive Cruise | Yes | Yes | 0mph | 7mph |
|
||||
| Chevrolet<sup>3</sup> | Malibu 2017<sup>6</sup> | Adaptive Cruise | Yes | Yes | 0mph | 7mph |
|
||||
| Chevrolet<sup>3</sup> | Volt 2017-18<sup>6</sup> | Adaptive Cruise | Yes | Yes | 0mph | 7mph |
|
||||
| Cadillac<sup>3</sup> | ATS 2018 | Adaptive Cruise | Yes | Yes | 0mph | 7mph |
|
||||
| Chrysler | Pacifica 2017-18<sup>7</sup> | Adaptive Cruise | Yes | Stock | 0mph | 9mph |
|
||||
| Chrysler | Pacifica Hybrid 2017-18<sup>7</sup>| Adaptive Cruise | Yes | Stock | 0mph | 9mph |
|
||||
| Chrysler | Pacifica Hybrid 2019<sup>7</sup> | Adaptive Cruise | Yes | Stock | 0mph | 39mph |
|
||||
| GMC<sup>3</sup> | Acadia Denali 2018<sup>6</sup> | Adaptive Cruise | Yes | Yes | 0mph | 7mph |
|
||||
| Holden<sup>3</sup> | Astra 2017<sup>6</sup> | Adaptive Cruise | Yes | Yes | 0mph | 7mph |
|
||||
| Honda | Accord 2018-19 | All | Yes | Stock | 0mph | 3mph |
|
||||
| Honda | Accord Hybrid 2018-19 | All | Yes | Stock | 0mph | 3mph |
|
||||
| Honda | Civic Sedan/Coupe 2016-18 | Honda Sensing | Yes | Yes | 0mph | 12mph |
|
||||
| Honda | Civic Sedan/Coupe 2019 | Honda Sensing | Yes | Stock | 0mph | 2mph |
|
||||
| Honda | Civic Hatchback 2017-19 | Honda Sensing | Yes | Stock | 0mph | 12mph |
|
||||
| Honda | CR-V 2015-16 | Touring | Yes | Yes | 25mph<sup>1</sup>| 12mph |
|
||||
| Honda | CR-V 2017-19 | Honda Sensing | Yes | Stock | 0mph | 12mph |
|
||||
| Honda | CR-V Hybrid 2017-2019 | Honda Sensing | Yes | Stock | 0mph | 12mph |
|
||||
| Honda | Fit 2018-19 | Honda Sensing | Yes | Yes | 25mph<sup>1</sup>| 12mph |
|
||||
| Honda | Odyssey 2018-19 | Honda Sensing | Yes | Yes | 25mph<sup>1</sup>| 0mph |
|
||||
| Honda | Passport 2019 | All | Yes | Yes | 25mph<sup>1</sup>| 12mph |
|
||||
| Honda | Pilot 2016-18 | Honda Sensing | Yes | Yes | 25mph<sup>1</sup>| 12mph |
|
||||
| Honda | Pilot 2019 | All | Yes | Yes | 25mph<sup>1</sup>| 12mph |
|
||||
| Honda | Ridgeline 2017-19 | Honda Sensing | Yes | Yes | 25mph<sup>1</sup>| 12mph |
|
||||
| Hyundai | Santa Fe 2019<sup>5</sup> | All | Yes | Stock | 0mph | 0mph |
|
||||
| Hyundai | Elantra 2017-19<sup>5</sup> | SCC + LKAS | Yes | Stock | 19mph | 34mph |
|
||||
| Hyundai | Genesis 2018<sup>5</sup> | All | Yes | Stock | 19mph | 34mph |
|
||||
| Jeep | Grand Cherokee 2016-18<sup>7</sup> | Adaptive Cruise | Yes | Stock | 0mph | 9mph |
|
||||
| Jeep | Grand Cherokee 2019<sup>7</sup> | Adaptive Cruise | Yes | Stock | 0mph | 39mph |
|
||||
| Kia | Optima 2019<sup>5</sup> | SCC + LKAS | Yes | Stock | 0mph | 0mph |
|
||||
| Kia | Sorento 2018<sup>5</sup> | All | Yes | Stock | 0mph | 0mph |
|
||||
| Kia | Stinger 2018<sup>5</sup> | SCC + LKAS | Yes | Stock | 0mph | 0mph |
|
||||
| Lexus | CT Hybrid 2017-18 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph |
|
||||
| Lexus | ES Hybrid 2019 | All | Yes | Yes | 0mph | 0mph |
|
||||
| Lexus | RX Hybrid 2016-19 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph |
|
||||
| Lexus | IS 2017-2019 | All | Yes | Stock | 22mph | 0mph |
|
||||
| Lexus | IS Hybrid 2017 | All | Yes | Stock | 0mph | 0mph |
|
||||
| Subaru | Crosstrek 2018-19 | EyeSight | Yes | Stock | 0mph | 0mph |
|
||||
| Subaru | Impreza 2019-20 | EyeSight | Yes | Stock | 0mph | 0mph |
|
||||
| Toyota | Avalon 2016 | TSS-P | Yes | Yes<sup>2</sup>| 20mph<sup>1</sup>| 0mph |
|
||||
| Toyota | Avalon 2017-18 | All | Yes | Yes<sup>2</sup>| 20mph<sup>1</sup>| 0mph |
|
||||
| Toyota | Camry 2018-19 | All | Yes | Stock | 0mph<sup>4</sup> | 0mph |
|
||||
| Toyota | Camry Hybrid 2018-19 | All | Yes | Stock | 0mph<sup>4</sup> | 0mph |
|
||||
| Toyota | C-HR 2017-19 | All | Yes | Stock | 0mph | 0mph |
|
||||
| Toyota | C-HR Hybrid 2017-19 | All | Yes | Stock | 0mph | 0mph |
|
||||
| Toyota | Corolla 2017-19 | All | Yes | Yes<sup>2</sup>| 20mph<sup>1</sup>| 0mph |
|
||||
| Toyota | Corolla 2020 | All | Yes | Yes | 0mph | 0mph |
|
||||
| Toyota | Corolla Hatchback 2019 | All | Yes | Yes | 0mph | 0mph |
|
||||
| Toyota | Corolla Hybrid 2020 | All | Yes | Yes | 0mph | 0mph |
|
||||
| Toyota | Highlander 2017-19 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph |
|
||||
| Toyota | Highlander Hybrid 2017-19 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph |
|
||||
| Toyota | Prius 2016 | TSS-P | Yes | Yes<sup>2</sup>| 0mph | 0mph |
|
||||
| Toyota | Prius 2017-19 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph |
|
||||
| Toyota | Prius Prime 2017-20 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph |
|
||||
| Toyota | Rav4 2016 | TSS-P | Yes | Yes<sup>2</sup>| 20mph<sup>1</sup>| 0mph |
|
||||
| Toyota | Rav4 2017-18 | All | Yes | Yes<sup>2</sup>| 20mph<sup>1</sup>| 0mph |
|
||||
| Toyota | Rav4 2019 | All | Yes | Yes | 0mph | 0mph |
|
||||
| Toyota | Rav4 Hybrid 2016 | TSS-P | Yes | Yes<sup>2</sup>| 0mph | 0mph |
|
||||
| Toyota | Rav4 Hybrid 2017-18 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph |
|
||||
| Toyota | Sienna 2018 | All | Yes | Yes<sup>2</sup>| 0mph | 0mph |
|
||||
| Volkswagen<sup>8</sup>| Golf 2016-19 | Driver Assistance | Yes | Stock | 0mph | 0mph |
|
||||
|
||||
<sup>1</sup>[Comma Pedal](https://community.comma.ai/wiki/index.php/Comma_Pedal) is used to provide stop-and-go capability to some of the openpilot-supported cars that don't currently support stop-and-go. Here is how to [build a Comma Pedal](https://medium.com/@jfrux/comma-pedal-building-with-macrofab-6328bea791e8). ***NOTE: The Comma Pedal is not officially supported by [comma.ai](https://comma.ai).*** <br />
|
||||
<sup>2</sup>When disconnecting the Driver Support Unit (DSU), otherwise longitudinal control is stock ACC. For DSU locations, see [Toyota Wiki page](https://community.comma.ai/wiki/index.php/Toyota). ***NOTE: disconnecting the DSU disables Automatic Emergency Braking (AEB).*** <br />
|
||||
<sup>3</sup>[GM installation guide](https://zoneos.com/volt/). ***NOTE: disconnecting the ASCM disables Automatic Emergency Braking (AEB).*** <br />
|
||||
<sup>4</sup>28mph for Camry 4CYL L, 4CYL LE and 4CYL SE which don't have Full-Speed Range Dynamic Radar Cruise Control. <br />
|
||||
<sup>5</sup>Requires a [panda](https://comma.ai/shop/products/panda-obd-ii-dongle) and open sourced [Hyundai Giraffe](https://github.com/commaai/neo/tree/master/giraffe/hyundai), designed for the 2019 Sante Fe; pinout may differ for other Hyundais. <br />
|
||||
<sup>6</sup>Requires a [panda](https://comma.ai/shop/products/panda-obd-ii-dongle) and community built giraffe, find more information [here](https://zoneos.com/shop/). <br />
|
||||
<sup>7</sup>Requires a [panda](https://comma.ai/shop/products/panda-obd-ii-dongle) and FCA [giraffe](https://comma.ai/shop/products/giraffe) <br />
|
||||
<sup>8</sup>Requires a [custom connector](https://community.comma.ai/wiki/index.php/Volkswagen#Integration_at_J533_Gateway) for the [car harness](https://comma.ai/shop/products/car-harness) <br />
|
||||
|
||||
Community Maintained Cars
|
||||
------
|
||||
|
||||
| Make | Model (US Market Reference) | Supported Package | Lateral | Longitudinal | No Accel Below | No Steer Below |
|
||||
| ---------------------| -----------------------------------| ---------------------| --------| ---------------| -----------------| ---------------|
|
||||
| Tesla | Model S 2012-13<sup>9</sup> | All | Yes | NA | NA | 0mph |
|
||||
|
||||
[[Tesla Model S Pull Request]](https://github.com/commaai/openpilot/pull/246) <br />
|
||||
<sup>9</sup>Requires a [panda](https://comma.ai/shop/products/panda-obd-ii-dongle) and community built giraffe, find more information [here](https://github.com/jeankalud/neo/tree/tesla_giraffe/giraffe/tesla). <br />
|
||||
|
||||
Community Maintained Cars are not confirmed by comma.ai to meet our [safety model](https://github.com/commaai/openpilot/blob/devel/SAFETY.md). Be extra cautious using them.
|
||||
|
||||
In Progress Cars
|
||||
------
|
||||
- All TSS-P Toyota with Steering Assist and LSS-P Lexus with Steering Assist or Lane Keep Assist.
|
||||
- All Hyundai with SmartSense.
|
||||
- All Kia, Genesis with SCC and LKAS.
|
||||
- All Chrysler, Jeep, Fiat with Adaptive Cruise Control and LaneSense.
|
||||
- All Subaru with EyeSight.
|
||||
- All Volkswagen, Audi, Škoda and SEAT with Adaptive Cruise Control.
|
||||
|
||||
How can I add support for my car?
|
||||
------
|
||||
|
||||
If your car has adaptive cruise control and lane keep assist, you are in luck. Using a [panda](https://comma.ai/shop/products/panda-obd-ii-dongle/) and [cabana](https://community.comma.ai/cabana/), you can understand how to make your car drive by wire.
|
||||
|
||||
We've written guides for [Brand](https://medium.com/@comma_ai/how-to-write-a-car-port-for-openpilot-7ce0785eda84) and [Model](https://medium.com/@comma_ai/openpilot-port-guide-for-toyota-models-e5467f4b5fe6) ports. These guides might help you after you have the basics figured out.
|
||||
|
||||
- BMW, Audi, Volvo, and Mercedes all use [FlexRay](https://en.wikipedia.org/wiki/FlexRay) and can be supported after [FlexRay support](https://github.com/commaai/openpilot/pull/463) is merged.
|
||||
- We put time into a Ford port, but the steering has a 10 second cutout limitation that makes it unusable.
|
||||
- The 2016-2017 Honda Accord uses a custom signaling protocol for steering that's unlikely to ever be upstreamed.
|
||||
|
||||
Directory structure
|
||||
------
|
||||
.
|
||||
├── apk # The apk files used for the UI
|
||||
├── cereal # The messaging spec used for all logs on EON
|
||||
├── common # Library like functionality we've developed here
|
||||
├── installer/updater # Manages auto-updates of openpilot
|
||||
├── opendbc # Files showing how to interpret data from cars
|
||||
├── panda # Code used to communicate on CAN and LIN
|
||||
├── phonelibs # Libraries used on EON
|
||||
├── pyextra # Libraries used on EON
|
||||
└── selfdrive # Code needed to drive the car
|
||||
├── assets # Fonts and images for UI
|
||||
├── athena # Allows communication with the app
|
||||
├── boardd # Daemon to talk to the board
|
||||
├── can # Helpers for parsing CAN messages
|
||||
├── car # Car specific code to read states and control actuators
|
||||
├── common # Shared C/C++ code for the daemons
|
||||
├── controls # Perception, planning and controls
|
||||
├── debug # Tools to help you debug and do car ports
|
||||
├── locationd # Soon to be home of precise location
|
||||
├── logcatd # Android logcat as a service
|
||||
├── loggerd # Logger and uploader of car data
|
||||
├── proclogd # Logs information from proc
|
||||
├── sensord # IMU / GPS interface code
|
||||
├── test # Car simulator running code through virtual maneuvers
|
||||
├── ui # The UI
|
||||
└── visiond # Vision pipeline
|
||||
|
||||
To understand how the services interact, see `selfdrive/service_list.yaml`
|
||||
|
||||
User Data / chffr Account / Crash Reporting
|
||||
------
|
||||
|
||||
By default, openpilot creates an account and includes a client for chffr, our dashcam app. We use your data to train better models and improve openpilot for everyone.
|
||||
|
||||
It's open source software, so you are free to disable it if you wish.
|
||||
|
||||
It logs the road facing camera, CAN, GPS, IMU, magnetometer, thermal sensors, crashes, and operating system logs.
|
||||
The user facing camera is only logged if you explicitly opt-in in settings.
|
||||
It does not log the microphone.
|
||||
|
||||
By using it, you agree to [our privacy policy](https://my.comma.ai/privacy). You understand that use of this software or its related services will generate certain types of user data, which may be logged and stored at the sole discretion of comma.ai. By accepting this agreement, you grant an irrevocable, perpetual, worldwide right to comma.ai for the use of this data.
|
||||
|
||||
Testing on PC
|
||||
------
|
||||
|
||||
Check out [openpilot-tools](https://github.com/commaai/openpilot-tools): lots of tools you can use to replay driving data, test and develop openpilot from your pc.
|
||||
|
||||
Also, within openpilot there is a rudimentary infrastructure to run a basic simulation and generate a report of openpilot's behavior in different longitudinal control scenarios.
|
||||
|
||||
```bash
|
||||
# Requires working docker
|
||||
./run_docker_tests.sh
|
||||
```
|
||||
|
||||
Contributing
|
||||
------
|
||||
|
||||
We welcome both pull requests and issues on [github](http://github.com/commaai/openpilot). Bug fixes and new car ports encouraged.
|
||||
|
||||
We also have a [bounty program](https://comma.ai/bounties.html).
|
||||
|
||||
Want to get paid to work on openpilot? [comma.ai is hiring](https://comma.ai/jobs/)
|
||||
|
||||
Licensing
|
||||
------
|
||||
|
||||
openpilot is released under the MIT license. Some parts of the software are released under other licenses as specified.
|
||||
|
||||
Any user of this software shall indemnify and hold harmless Comma.ai, Inc. and its directors, officers, employees, agents, stockholders, affiliates, subcontractors and customers from and against all allegations, claims, actions, suits, demands, damages, liabilities, obligations, losses, settlements, judgments, costs and expenses (including without limitation attorneys’ fees and costs) which arise out of, relate to or result from any use of this software by user.
|
||||
|
||||
**THIS IS ALPHA QUALITY SOFTWARE FOR RESEARCH PURPOSES ONLY. THIS IS NOT A PRODUCT.
|
||||
YOU ARE RESPONSIBLE FOR COMPLYING WITH LOCAL LAWS AND REGULATIONS.
|
||||
NO WARRANTY EXPRESSED OR IMPLIED.**
|
||||
|
||||
---
|
||||
|
||||
<img src="https://d1qb2nb5cznatu.cloudfront.net/startups/i/1061157-bc7e9bf3b246ece7322e6ffe653f6af8-medium_jpg.jpg?buster=1458363130" width="75"></img> <img src="https://cdn-images-1.medium.com/max/1600/1*C87EjxGeMPrkTuVRVWVg4w.png" width="225"></img>
|
||||
36
README_chffrplus.md
Normal file
36
README_chffrplus.md
Normal file
@@ -0,0 +1,36 @@
|
||||
Welcome to chffrplus
|
||||
======
|
||||
|
||||
[chffrplus](https://github.com/commaai/chffrplus) is an open source dashcam.
|
||||
|
||||
This is the shipping reference software for the comma EON Dashcam DevKit. It keeps many of the niceities of [openpilot](https://github.com/commaai/openpilot), like high quality sensors, great camera, and good autostart and stop. Though unlike openpilot, it cannot control your car. chffrplus can interface with your car through a [panda](https://shop.comma.ai/products/panda-obd-ii-dongle), but just like our dashcam app [chffr](https://getchffr.com/), it is read only.
|
||||
|
||||
It integrates with the rest of the comma ecosystem, so you can view your drives on the [chffr](https://getchffr.com/) app for Android or iOS, and reverse engineer your car with [cabana](https://community.comma.ai/cabana/?demo=1).
|
||||
|
||||
|
||||
Hardware
|
||||
------
|
||||
|
||||
Right now chffrplus supports the [EON Dashcam DevKit](https://shop.comma.ai/products/eon-dashcam-devkit) for hardware to run on.
|
||||
|
||||
Install chffrplus on a EON device by entering ``https://chffrplus.comma.ai`` during NEOS setup.
|
||||
|
||||
|
||||
User Data / chffr Account / Crash Reporting
|
||||
------
|
||||
|
||||
By default chffrplus creates an account and includes a client for chffr, our dashcam app.
|
||||
|
||||
It's open source software, so you are free to disable it if you wish.
|
||||
|
||||
It logs the road facing camera, CAN, GPS, IMU, magnetometer, thermal sensors, crashes, and operating system logs.
|
||||
It does not log the user facing camera or the microphone.
|
||||
|
||||
By using it, you agree to [our privacy policy](https://beta.comma.ai/privacy.html). You understand that use of this software or its related services will generate certain types of user data, which may be logged and stored at the sole discretion of comma.ai. By accepting this agreement, you grant an irrevocable, perpetual, worldwide right to comma.ai for the use of this data.
|
||||
|
||||
|
||||
Licensing
|
||||
------
|
||||
|
||||
chffrplus is released under the MIT license.
|
||||
|
||||
476
RELEASES.md
Normal file
476
RELEASES.md
Normal file
@@ -0,0 +1,476 @@
|
||||
Version 0.6.6 (2019-11-05)
|
||||
========================
|
||||
* Volkswagen support thanks to jyoung8607!
|
||||
* Toyota Corolla Hybrid with TSS 2.0 support thanks to u8511049!
|
||||
* Lexus ES with TSS 2.0 support thanks to energee!
|
||||
* Fix GM ignition detection and lock safety mode not required anymore
|
||||
* Log panda firmware and dongle ID thanks to martinl!
|
||||
* New driving model: improve path prediction and lead detection
|
||||
* New driver monitoring model, 4x smaller and running on DSP
|
||||
* Display an alert and don't start openpilot if panda has wrong firmware
|
||||
* Fix bug preventing EON from terminating processes after a drive
|
||||
* Remove support for Toyota giraffe without the 120Ohm resistor
|
||||
|
||||
Version 0.6.5 (2019-10-07)
|
||||
========================
|
||||
* NEOS update: upgrade to Python3 and new installer!
|
||||
* comma Harness support!
|
||||
* New driving model: improve path prediction
|
||||
* New driver monitoring model: more accurate face and eye detection
|
||||
* Redesign offroad screen to display updates and alerts
|
||||
* Increase maximum allowed acceleration
|
||||
* Prevent car 12V battery drain by cutting off EON charge after 3 days of no drive
|
||||
* Lexus CT Hybrid support thanks to thomaspich!
|
||||
* Louder chime for critical alerts
|
||||
* Add toggle to switch to dashcam mode
|
||||
* Fix "invalid vehicle params" error on DSU-less Toyota
|
||||
|
||||
Version 0.6.4 (2019-09-08)
|
||||
========================
|
||||
* Forward stock AEB for Honda Nidec
|
||||
* Improve lane centering on banked roads
|
||||
* Always-on forward collision warning
|
||||
* Always-on driver monitoring, except for right hand drive countries
|
||||
* Driver monitoring learns the user's normal driving position
|
||||
* Honda Fit support thanks to energee!
|
||||
* Lexus IS support
|
||||
|
||||
Version 0.6.3 (2019-08-12)
|
||||
========================
|
||||
* Alert sounds from EON: requires NEOS update
|
||||
* Improve driver monitoring: eye tracking and improved awareness logic
|
||||
* Improve path prediction with new driving model
|
||||
* Improve lane positioning with wide lanes and exits
|
||||
* Improve lateral control on RAV4
|
||||
* Slow down for turns using model
|
||||
* Open sourced regression test to verify outputs against reference logs
|
||||
* Open sourced regression test to sanity check all car models
|
||||
|
||||
Version 0.6.2 (2019-07-29)
|
||||
========================
|
||||
* New driving model!
|
||||
* Improve lane tracking with double lines
|
||||
* Strongly improve stationary vehicle detection
|
||||
* Strongly reduce cases of braking due to false leads
|
||||
* Better lead tracking around turns
|
||||
* Improve cut-in prediction by using neural network
|
||||
* Improve lateral control on Toyota Camry and C-HR thanks to zorrobyte!
|
||||
* Fix unintended openpilot disengagements on Jeep thanks to adhintz!
|
||||
* Fix delayed transition to offroad when car is turned off
|
||||
|
||||
Version 0.6.1 (2019-07-21)
|
||||
========================
|
||||
* Remote SSH with comma prime and [ssh.comma.ai](https://ssh.comma.ai)
|
||||
* Panda code Misra-c2012 compliance, tested against cppcheck coverage
|
||||
* Lockout openpilot after 3 terminal alerts for driver distracted or unresponsive
|
||||
* Toyota Sienna support thanks to wocsor!
|
||||
|
||||
Version 0.6 (2019-07-01)
|
||||
========================
|
||||
* New model, with double the pixels and ten times the temporal context!
|
||||
* Car should not take exits when in the right lane
|
||||
* openpilot uses only ~65% of the CPU (down from 75%)
|
||||
* Routes visible in connect/explorer after only 0.2% is uploaded (qlogs)
|
||||
* loggerd and sensord are open source, every line of openpilot is now open
|
||||
* Panda safety code is MISRA compliant and ships with a signed version on release2
|
||||
* New NEOS is 500MB smaller and has a reproducible usr/pipenv
|
||||
* Lexus ES Hybrid support thanks to wocsor!
|
||||
* Improve tuning for supported Toyota with TSS 2.0
|
||||
* Various other stability improvements
|
||||
|
||||
Version 0.5.13 (2019-05-31)
|
||||
==========================
|
||||
* Reduce panda power consumption by 70%, down to 80mW, when car is off (not for GM)
|
||||
* Reduce EON power consumption by 40%, down to 1100mW, when car is off
|
||||
* Reduce CPU utilization by 20% and improve stability
|
||||
* Temporarily remove mapd functionalities to improve stability
|
||||
* Add openpilot record-only mode for unsupported cars
|
||||
* Synchronize controlsd to boardd to reduce latency
|
||||
* Remove panda support for Subaru giraffe
|
||||
|
||||
Version 0.5.12 (2019-05-16)
|
||||
==========================
|
||||
* Improve lateral control for the Prius and Prius Prime
|
||||
* Compress logs before writing to disk
|
||||
* Remove old driving data when storage reaches 90% full
|
||||
* Fix small offset in following distance
|
||||
* Various small CPU optimizations
|
||||
* Improve offroad power consumption: require NEOS Update
|
||||
* Add default speed limits for Estonia thanks to martinl!
|
||||
* Subaru Crosstrek support thanks to martinl!
|
||||
* Toyota Avalon support thanks to njbrown09!
|
||||
* Toyota Rav4 with TSS 2.0 support thanks to wocsor!
|
||||
* Toyota Corolla with TSS 2.0 support thanks to wocsor!
|
||||
|
||||
Version 0.5.11 (2019-04-17)
|
||||
========================
|
||||
* Add support for Subaru
|
||||
* Reduce panda power consumption by 60% when car is off
|
||||
* Fix controlsd lag every 6 minutes. This would sometimes cause disengagements
|
||||
* Fix bug in controls with new angle-offset learner in MPC
|
||||
* Reduce cpu consumption of ubloxd by rewriting it in C++
|
||||
* Improve driver monitoring model and face detection
|
||||
* Improve performance of visiond and ui
|
||||
* Honda Passport 2019 support
|
||||
* Lexus RX Hybrid 2019 support thanks to schomems!
|
||||
* Improve road selection heuristic in mapd
|
||||
* Add Lane Departure Warning to dashboard for Toyota thanks to arne182
|
||||
|
||||
Version 0.5.10 (2019-03-19)
|
||||
========================
|
||||
* Self-tuning vehicle parameters: steering offset, tire stiffness and steering ratio
|
||||
* Improve longitudinal control at low speed when lead vehicle harshly decelerates
|
||||
* Fix panda bug going unexpectedly in DCP mode when EON is connected
|
||||
* Reduce white panda power consumption by 500mW when EON is disconnected by turning off WIFI
|
||||
* New Driver Monitoring Model
|
||||
* Support QR codes for login using comma connect
|
||||
* Refactor comma pedal FW and use CRC-8 checksum algorithm for safety. Reflashing pedal is required.
|
||||
Please see `#hw-pedal` on [discord](discord.comma.ai) for assistance updating comma pedal.
|
||||
* Additional speed limit rules for Germany thanks to arne182
|
||||
* Allow negative speed limit offsets
|
||||
|
||||
Version 0.5.9 (2019-02-10)
|
||||
========================
|
||||
* Improve calibration using a dedicated neural network
|
||||
* Abstract planner in its own process to remove lags in controls process
|
||||
* Improve speed limits with country/region defaults by road type
|
||||
* Reduce mapd data usage with gzip thanks to eFiniLan
|
||||
* Zip log files in the background to reduce disk usage
|
||||
* Kia Optima support thanks to emmertex!
|
||||
* Buick Regal 2018 support thanks to HOYS!
|
||||
* Comma pedal support for Toyota thanks to wocsor! Note: tuning needed and not maintained by comma
|
||||
* Chrysler Pacifica and Jeep Grand Cherokee support thanks to adhintz!
|
||||
|
||||
Version 0.5.8 (2019-01-17)
|
||||
========================
|
||||
* Open sourced visiond
|
||||
* Auto-slowdown for upcoming turns
|
||||
* Chrysler/Jeep/Fiat support thanks to adhintz!
|
||||
* Honda Civic 2019 support thanks to csouers!
|
||||
* Improve use of car display in Toyota thanks to arne182!
|
||||
* No data upload when connected to Android or iOS hotspots and "Enable Upload Over Cellular" setting is off
|
||||
* EON stops charging when 12V battery drops below 11.8V
|
||||
|
||||
Version 0.5.7 (2018-12-06)
|
||||
========================
|
||||
* Speed limit from OpenStreetMap added to UI
|
||||
* Highlight speed limit when speed exceeds road speed limit plus a delta
|
||||
* Option to limit openpilot max speed to road speed limit plus a delta
|
||||
* Cadillac ATS support thanks to vntarasov!
|
||||
* GMC Acadia support thanks to CryptoKylan!
|
||||
* Decrease GPU power consumption
|
||||
* NEOSv8 autoupdate
|
||||
|
||||
Version 0.5.6 (2018-11-16)
|
||||
========================
|
||||
* Refresh settings layout and add feature descriptions
|
||||
* In Honda, keep stock camera on for logging and extra stock features; new openpilot giraffe setting is 0111!
|
||||
* In Toyota, option to keep stock camera on for logging and extra stock features (e.g. AHB); 120Ohm resistor required on giraffe.
|
||||
* Improve camera calibration stability
|
||||
* More tuning to Honda positive accelerations
|
||||
* Reduce brake pump use on Hondas
|
||||
* Chevrolet Malibu support thanks to tylergets!
|
||||
* Holden Astra support thanks to AlexHill!
|
||||
|
||||
Version 0.5.5 (2018-10-20)
|
||||
========================
|
||||
* Increase allowed Honda positive accelerations
|
||||
* Fix sporadic unexpected braking when passing semi-trucks in Toyota
|
||||
* Fix gear reading bug in Hyundai Elantra thanks to emmertex!
|
||||
|
||||
Version 0.5.4 (2018-09-25)
|
||||
========================
|
||||
* New Driving Model
|
||||
* New Driver Monitoring Model
|
||||
* Improve longitudinal mpc in mid-low speed braking
|
||||
* Honda Accord hybrid support thanks to energee!
|
||||
* Ship mpc binaries and sensibly reduce build time
|
||||
* Calibration more stable
|
||||
* More Hyundai and Kia cars supported thanks to emmertex!
|
||||
* Various GM Volt improvements thanks to vntarasov!
|
||||
|
||||
Version 0.5.3 (2018-09-03)
|
||||
========================
|
||||
* Hyundai Santa Fe support!
|
||||
* Honda Pilot 2019 support thanks to energee!
|
||||
* Toyota Highlander support thanks to daehahn!
|
||||
* Improve steering tuning for Honda Odyssey
|
||||
|
||||
Version 0.5.2 (2018-08-16)
|
||||
========================
|
||||
* New calibration: more accurate, a lot faster, open source!
|
||||
* Enable orbd
|
||||
* Add little endian support to CAN packer
|
||||
* Fix fingerprint for Honda Accord 1.5T
|
||||
* Improve driver monitoring model
|
||||
|
||||
Version 0.5.1 (2018-08-01)
|
||||
========================
|
||||
* Fix radar error on Civic sedan 2018
|
||||
* Improve thermal management logic
|
||||
* Alpha Toyota C-HR and Camry support!
|
||||
* Auto-switch Driver Monitoring to 3 min counter when inaccurate
|
||||
|
||||
Version 0.5 (2018-07-11)
|
||||
========================
|
||||
* Driver Monitoring (beta) option in settings!
|
||||
* Make visiond, loggerd and UI use less resources
|
||||
* 60 FPS UI
|
||||
* Better car parameters for most cars
|
||||
* New sidebar with stats
|
||||
* Remove Waze and Spotify to free up system resources
|
||||
* Remove rear view mirror option
|
||||
* Calibration 3x faster
|
||||
|
||||
Version 0.4.7.2 (2018-06-25)
|
||||
==========================
|
||||
* Fix loggerd lag issue
|
||||
* No longer prompt for updates
|
||||
* Mitigate right lane hugging for properly mounted EON (procedure on wiki)
|
||||
|
||||
Version 0.4.7.1 (2018-06-18)
|
||||
==========================
|
||||
* Fix Acura ILX steer faults
|
||||
* Fix bug in mock car
|
||||
|
||||
Version 0.4.7 (2018-06-15)
|
||||
==========================
|
||||
* New model!
|
||||
* GM Volt (and CT6 lateral) support!
|
||||
* Honda Bosch lateral support!
|
||||
* Improve actuator modeling to reduce lateral wobble
|
||||
* Minor refactor of car abstraction layer
|
||||
* Hack around orbd startup issue
|
||||
|
||||
Version 0.4.6 (2018-05-18)
|
||||
==========================
|
||||
* NEOSv6 required! Will autoupdate
|
||||
* Stability improvements
|
||||
* Fix all memory leaks
|
||||
* Update C++ compiler to clang6
|
||||
* Improve front camera exposure
|
||||
|
||||
Version 0.4.5 (2018-04-27)
|
||||
==========================
|
||||
* Release notes added to the update popup
|
||||
* Improve auto shut-off logic to disallow empty battery
|
||||
* Added onboarding instructions
|
||||
* Include orbd, the first piece of new calibration algorithm
|
||||
* Show remaining upload data instead of file numbers
|
||||
* Fix UI bugs
|
||||
* Fix memory leaks
|
||||
|
||||
Version 0.4.4 (2018-04-13)
|
||||
==========================
|
||||
* EON are flipped! Flip your EON's mount!
|
||||
* Alpha Honda Ridgeline support thanks to energee!
|
||||
* Support optional front camera recording
|
||||
* Upload over cellular toggle now applies to all files, not just video
|
||||
* Increase acceleration when closing lead gap
|
||||
* User now prompted for future updates
|
||||
* NEO no longer supported :(
|
||||
|
||||
Version 0.4.3.2 (2018-03-29)
|
||||
============================
|
||||
* Improve autofocus
|
||||
* Improve driving when only one lane line is detected
|
||||
* Added fingerprint for Toyota Corolla LE
|
||||
* Fixed Toyota Corolla steer error
|
||||
* Full-screen driving UI
|
||||
* Improved path drawing
|
||||
|
||||
Version 0.4.3.1 (2018-03-19)
|
||||
============================
|
||||
* Improve autofocus
|
||||
* Add check for MPC solution error
|
||||
* Make first distracted warning visual only
|
||||
|
||||
Version 0.4.3 (2018-03-13)
|
||||
==========================
|
||||
* Add HDR and autofocus
|
||||
* Update UI aesthetic
|
||||
* Grey panda works in Waze
|
||||
* Add alpha support for 2017 Honda Pilot
|
||||
* Slight increase in acceleration response from stop
|
||||
* Switch CAN sending to use CANPacker
|
||||
* Fix pulsing acceleration regression on Honda
|
||||
* Fix openpilot bugs when stock system is in use
|
||||
* Change starting logic for chffrplus to use battery voltage
|
||||
|
||||
Version 0.4.2 (2018-02-05)
|
||||
==========================
|
||||
* Add alpha support for 2017 Lexus RX Hybrid
|
||||
* Add alpha support for 2018 ACURA RDX
|
||||
* Updated fingerprint to include Toyota Rav4 SE and Prius Prime
|
||||
* Bugfixes for Acura ILX and Honda Odyssey
|
||||
|
||||
Version 0.4.1 (2018-01-30)
|
||||
==========================
|
||||
* Add alpha support for 2017 Toyota Corolla
|
||||
* Add alpha support for 2018 Honda Odyssey with Honda Sensing
|
||||
* Add alpha support for Grey Panda
|
||||
* Refactored car abstraction layer to make car ports easier
|
||||
* Increased steering torque limit on Honda CR-V by 30%
|
||||
|
||||
Version 0.4.0.2 (2018-01-18)
|
||||
==========================
|
||||
* Add focus adjustment slider
|
||||
* Minor bugfixes
|
||||
|
||||
Version 0.4.0.1 (2017-12-21)
|
||||
==========================
|
||||
* New UI to match chffrplus
|
||||
* Improved lateral control tuning to fix oscillations on Civic
|
||||
* Add alpha support for 2017 Toyota Rav4 Hybrid
|
||||
* Reduced CPU usage
|
||||
* Removed unnecessary utilization of fan at max speed
|
||||
* Minor bug fixes
|
||||
|
||||
Version 0.3.9 (2017-11-21)
|
||||
==========================
|
||||
* Add alpha support for 2017 Toyota Prius
|
||||
* Improved longitudinal control using model predictive control
|
||||
* Enable Forward Collision Warning
|
||||
* Acura ILX now maintains openpilot engaged at standstill when brakes are applied
|
||||
|
||||
Version 0.3.8.2 (2017-10-30)
|
||||
==========================
|
||||
* Add alpha support for 2017 Toyota RAV4
|
||||
* Smoother lateral control
|
||||
* Stay silent if stock system is connected through giraffe
|
||||
* Minor bug fixes
|
||||
|
||||
Version 0.3.7 (2017-09-30)
|
||||
==========================
|
||||
* Improved lateral control using model predictive control
|
||||
* Improved lane centering
|
||||
* Improved GPS
|
||||
* Reduced tendency of path deviation near right side exits
|
||||
* Enable engagement while the accelerator pedal is pressed
|
||||
* Enable engagement while the brake pedal is pressed, when stationary and with lead vehicle within 5m
|
||||
* Disable engagement when park brake or brake hold are active
|
||||
* Fixed sporadic longitudinal pulsing in Civic
|
||||
* Cleanups to vehicle interface
|
||||
|
||||
Version 0.3.6.1 (2017-08-15)
|
||||
============================
|
||||
* Mitigate low speed steering oscillations on some vehicles
|
||||
* Include board steering check for CR-V
|
||||
|
||||
Version 0.3.6 (2017-08-08)
|
||||
==========================
|
||||
* Fix alpha CR-V support
|
||||
* Improved GPS
|
||||
* Fix display of target speed not always matching HUD
|
||||
* Increased acceleration after stop
|
||||
* Mitigated some vehicles driving too close to the right line
|
||||
|
||||
Version 0.3.5 (2017-07-30)
|
||||
==========================
|
||||
* Fix bug where new devices would not begin calibration
|
||||
* Minor robustness improvements
|
||||
|
||||
Version 0.3.4 (2017-07-28)
|
||||
==========================
|
||||
* Improved model trained on more data
|
||||
* Much improved controls tuning
|
||||
* Performance improvements
|
||||
* Bugfixes and improvements to calibration
|
||||
* Driving log can play back video
|
||||
* Acura only: system now stays engaged below 25mph as long as brakes are applied
|
||||
|
||||
Version 0.3.3 (2017-06-28)
|
||||
===========================
|
||||
* Improved model trained on more data
|
||||
* Alpha CR-V support thanks to energee and johnnwvs!
|
||||
* Using the opendbc project for DBC files
|
||||
* Minor performance improvements
|
||||
* UI update thanks to pjlao307
|
||||
* Power off button
|
||||
* 6% more torque on the Civic
|
||||
|
||||
Version 0.3.2 (2017-05-22)
|
||||
===========================
|
||||
* Minor stability bugfixes
|
||||
* Added metrics and rear view mirror disable to settings
|
||||
* Update model with more crowdsourced data
|
||||
|
||||
Version 0.3.1 (2017-05-17)
|
||||
===========================
|
||||
* visiond stability bugfix
|
||||
* Add logging for angle and flashing
|
||||
|
||||
Version 0.3.0 (2017-05-12)
|
||||
===========================
|
||||
* Add CarParams struct to improve the abstraction layer
|
||||
* Refactor visiond IPC to support multiple clients
|
||||
* Add raw GPS and beginning support for navigation
|
||||
* Improve model in visiond using crowdsourced data
|
||||
* Add improved system logging to diagnose instability
|
||||
* Rewrite baseui in React Native
|
||||
* Moved calibration to the cloud
|
||||
|
||||
Version 0.2.9 (2017-03-01)
|
||||
===========================
|
||||
* Retain compatibility with NEOS v1
|
||||
|
||||
Version 0.2.8 (2017-02-27)
|
||||
===========================
|
||||
* Fix bug where frames were being dropped in minute 71
|
||||
|
||||
Version 0.2.7 (2017-02-08)
|
||||
===========================
|
||||
* Better performance and pictures at night
|
||||
* Fix ptr alignment issue in boardd
|
||||
* Fix brake error light, fix crash if too cold
|
||||
|
||||
Version 0.2.6 (2017-01-31)
|
||||
===========================
|
||||
* Fix bug in visiond model execution
|
||||
|
||||
Version 0.2.5 (2017-01-30)
|
||||
===========================
|
||||
* Fix race condition in manager
|
||||
|
||||
Version 0.2.4 (2017-01-27)
|
||||
===========================
|
||||
* OnePlus 3T support
|
||||
* Enable installation as NEOS app
|
||||
* Various minor bugfixes
|
||||
|
||||
Version 0.2.3 (2017-01-11)
|
||||
===========================
|
||||
* Reduce space usage by 80%
|
||||
* Add better logging
|
||||
* Add Travis CI
|
||||
|
||||
Version 0.2.2 (2017-01-10)
|
||||
===========================
|
||||
* Board triggers started signal on CAN messages
|
||||
* Improved autoexposure
|
||||
* Handle out of space, improve upload status
|
||||
|
||||
Version 0.2.1 (2016-12-14)
|
||||
===========================
|
||||
* Performance improvements, removal of more numpy
|
||||
* Fix boardd process priority
|
||||
* Make counter timer reset on use of steering wheel
|
||||
|
||||
Version 0.2 (2016-12-12)
|
||||
=========================
|
||||
* Car/Radar abstraction layers have shipped, see cereal/car.capnp
|
||||
* controlsd has been refactored
|
||||
* Shipped plant model and testing maneuvers
|
||||
* visiond exits more gracefully now
|
||||
* Hardware encoder in visiond should always init
|
||||
* ui now turns off the screen after 30 seconds
|
||||
* Switch to openpilot release branch for future releases
|
||||
* Added preliminary Docker container to run tests on PC
|
||||
|
||||
Version 0.1 (2016-11-29)
|
||||
=========================
|
||||
* Initial release of openpilot
|
||||
* Adaptive cruise control is working
|
||||
* Lane keep assist is working
|
||||
* Support for Acura ILX 2016 with AcuraWatch Plus
|
||||
* Support for Honda Civic 2016 Touring Edition
|
||||
174
SAFETY.md
Normal file
174
SAFETY.md
Normal file
@@ -0,0 +1,174 @@
|
||||
openpilot Safety
|
||||
======
|
||||
|
||||
openpilot is an Adaptive Cruise Control (ACC) and Automated Lane Centering (ALC) system.
|
||||
Like other ACC and ALC systems, openpilot requires the driver to be alert and to
|
||||
pay attention at all times. We repeat, **driver alertness is necessary, but not
|
||||
sufficient, for openpilot to be used safely**.
|
||||
|
||||
In order to enforce driver alertness, openpilot includes a driver monitoring feature
|
||||
that alerts the driver when distracted.
|
||||
|
||||
However, even with an attentive driver, we must make further efforts for the system to be
|
||||
safe. We have designed openpilot with two other safety considerations.
|
||||
|
||||
1. The driver must always be capable to immediately retake manual control of the vehicle,
|
||||
by stepping on either pedal or by pressing the cancel button.
|
||||
2. The vehicle must not alter its trajectory too quickly for the driver to safely
|
||||
react. This means that while the system is engaged, the actuators are constrained
|
||||
to operate within reasonable limits.
|
||||
|
||||
Following are details of the car specific safety implementations:
|
||||
|
||||
Honda/Acura
|
||||
------
|
||||
|
||||
- While the system is engaged, gas, brake and steer commands are subject to the same limits used by
|
||||
the stock system.
|
||||
|
||||
- Without an interceptor, the gas is controlled by the Powertrain Control Module (PCM).
|
||||
The PCM limits acceleration to what is reasonable for a cruise control system. With an
|
||||
interceptor, the gas is clipped to 60%.
|
||||
|
||||
- The brake is controlled by the 0x1FA CAN message. This message allows full
|
||||
braking, although the panda firmware and openpilot clip it to 1/4th of the max.
|
||||
This is approximately 0.3g of braking.
|
||||
|
||||
- Steering is controlled by the 0xE4 CAN message. The Electronic Power Steering (EPS)
|
||||
controller in the car limits the torque to a very small amount, so regardless of the
|
||||
message, the controller cannot jerk the wheel.
|
||||
|
||||
- Brake and gas pedal pressed signals are contained in the 0x17C CAN message. A rising edge of
|
||||
either signals triggers a disengagement, which is enforced by the panda firmware and by openpilot. The
|
||||
white led on the panda signifies if the panda is allowing control messages.
|
||||
|
||||
- Honda CAN uses both a counter and a checksum to ensure integrity and prevent
|
||||
replay of the same message.
|
||||
|
||||
Toyota/Lexus
|
||||
------
|
||||
|
||||
- While the system is engaged, gas, brake and steer commands are subject to the same limits used by
|
||||
the stock system.
|
||||
|
||||
- With the stock Driving Support Unit (DSU) connected (or in DSU-less models like Camry and C-HR),
|
||||
the acceleration is controlled by the stock system and is subject to the stock adaptive cruise
|
||||
control limits. Without the stock DSU connected, the acceleration command is controlled by the
|
||||
0x343 CAN message and its value is limited between .3g of deceleration and .15g of acceleration
|
||||
by the panda firmware and by openpilot. The acceleration command is ignored by the Engine Control
|
||||
Module (ECM) while the cruise control system is disengaged.
|
||||
|
||||
- Steering torque is controlled through the 0x2E4 CAN message and it's limited by the panda firmware and by
|
||||
openpilot to a value between -1500 and 1500. In addition, the vehicle EPS unit will not respond to
|
||||
commands outside these limits. A steering torque rate limit is enforced by the panda firmware and by
|
||||
openpilot, so that the commanded steering torque must rise from 0 to max value no faster than
|
||||
1.5s. Commanded steering torque is limited by the panda firmware and by openpilot to be no more than 350
|
||||
units above the actual EPS generated motor torque to ensure limited differences between
|
||||
commanded and actual torques.
|
||||
|
||||
- Brake and gas pedal pressed signals are contained in the 0x224 and 0x1D2 CAN messages,
|
||||
respectively. A rising edge of either signals triggers a disengagement, which is enforced by the
|
||||
panda firmware and by openpilot. Additionally, the cruise control system disengages on the rising edge of
|
||||
the brake pedal pressed signal.
|
||||
|
||||
- The cruise control system state is contained in the 0x1D2 message. No control messages are
|
||||
allowed if the cruise control system is not active. This is enforced by openpilot and the
|
||||
panda firmware. The white led on the panda signifies if the panda is allowing control messages.
|
||||
|
||||
GM/Chevrolet
|
||||
------
|
||||
|
||||
- While the system is engaged, gas, brake and steer commands are subject to the same limits used by
|
||||
the stock system.
|
||||
|
||||
- The gas and regen are controlled by the 0x2CB message and it's limited by the panda firmware and by
|
||||
openpilot to a value between 1404 and 3072. the minimum value correspond to a mild decel due to regen,
|
||||
while 3072 correspond to approximately 0.18g of acceleration from stop.
|
||||
|
||||
- The friction brakes are controlled by the 0x315 message and its value is limited by the panda firmware
|
||||
and openpilot to 350. This is approximately 0.3g of braking.
|
||||
|
||||
- Steering torque is controlled through the 0x180 CAN message and it's limited by the panda firmware and by
|
||||
openpilot to a value between -300 and 300. In addition, the vehicle EPS unit will fault for
|
||||
commands outside these limits. A steering torque rate limit is enforced by the panda firmware and by
|
||||
openpilot, so that the commanded steering torque must rise from 0 to max value no faster than
|
||||
0.75s. Commanded steering torque is gradually limited by the panda firmware and by openpilot if the driver's
|
||||
torque exceeds 12 units in the opposite direction to ensure limited applied torque against the
|
||||
driver's will.
|
||||
|
||||
- Brake pedal and gas pedal potentiometer signals are contained in the 0xF1 and 0x1A1 CAN messages,
|
||||
respectively. A rising edge of either signals triggers a disengagement, which is enforced by the
|
||||
panda firmware and by openpilot. Additionally, the cruise control system disengages on the rising edge of
|
||||
the brake pedal pressed signal. The regen paddle pressed signal is in the 0xBD message. When the
|
||||
regen paddle is pressed, a disengagement is enforced by both the firmware and by openpilot.
|
||||
|
||||
- GM CAN uses both a counter and a checksum to ensure integrity and prevent
|
||||
replay of the same message.
|
||||
|
||||
Hyundai/Kia (Lateral only)
|
||||
------
|
||||
|
||||
- While the system is engaged, steer commands are subject to the same limits used by
|
||||
the stock system.
|
||||
|
||||
- Steering torque is controlled through the 0x340 CAN message and it's limited by the panda firmware and by
|
||||
openpilot to a value between -255 and 255. In addition, the vehicle EPS unit will fault for
|
||||
commands outside the values of -409 and 409. A steering torque rate limit is enforced by the panda firmware and by
|
||||
openpilot, so that the commanded steering torque must rise from 0 to max value no faster than
|
||||
0.85s. Commanded steering torque is gradually limited by the panda firmware and by openpilot if the driver's
|
||||
torque exceeds 50 units in the opposite direction to ensure limited applied torque against the
|
||||
driver's will.
|
||||
|
||||
Chrysler/Jeep/Fiat (Lateral only)
|
||||
------
|
||||
|
||||
- While the system is engaged, steer commands are subject to the same limits used by
|
||||
the stock system.
|
||||
|
||||
- Steering torque is controlled through the 0x292 CAN message and it's limited by the panda firmware and by
|
||||
openpilot to a value between -261 and 261. In addition, the vehicle EPS unit will fault for
|
||||
commands outside these limits. A steering torque rate limit is enforced by the panda firmware and by
|
||||
openpilot, so that the commanded steering torque must rise from 0 to max value no faster than
|
||||
0.87s. Commanded steering torque is limited by the panda firmware and by openpilot to be no more than 80
|
||||
units above the actual EPS generated motor torque to ensure limited differences between
|
||||
commanded and actual torques.
|
||||
|
||||
Subaru (Lateral only)
|
||||
------
|
||||
|
||||
- While the system is engaged, steer commands are subject to the same limits used by
|
||||
the stock system.
|
||||
|
||||
- Steering torque is controlled through the 0x122 CAN message and it's limited by the panda firmware and by
|
||||
openpilot to a value between -255 and 255. In addition, the vehicle EPS unit will fault for
|
||||
commands outside the values of -2047 and 2047. A steering torque rate limit is enforced by the panda firmware and by
|
||||
openpilot, so that the commanded steering torque must rise from 0 to max value no faster than
|
||||
0.41s. Commanded steering torque is gradually limited by the panda firmware and by openpilot if the driver's
|
||||
torque exceeds 60 units in the opposite direction to ensure limited applied torque against the
|
||||
driver's will.
|
||||
|
||||
Volkswagen, Audi, SEAT, Škoda (Lateral only)
|
||||
------
|
||||
|
||||
- While the system is engaged, steer commands are subject to the same limits used by the stock system, and
|
||||
additional limits required to meet Comma safety standards.
|
||||
|
||||
- Steering torque is controlled through the CAN message 0x126, also known as HCA_01 for Heading Control Assist.
|
||||
It's limited by openpilot and Panda to a value between -250 and 250, representing 2.5 Nm of torque applied
|
||||
at the steering rack. The vehicle EPS unit will fault for values outside -300 and 300.
|
||||
|
||||
- The vehicle EPS unit will tolerate any rate of increase or decrease, but may limit the effective rate of
|
||||
change to 5.0 Nm/s. In accordance with the Comma AI safety model requirements, a rate limit is enforced by
|
||||
the Panda firmware and by openpilot, so that the commanded steering torque cannot rise from 0 to maximum
|
||||
faster than 1.25s. Commanded steering torque is gradually limited by the Panda firmware and by openpilot
|
||||
if the driver's torque exceeds 0.8 Nm in the opposite direction to ensure limited applied torque against
|
||||
the driver's will.
|
||||
|
||||
- Brake and gas pedal pressed signals are contained in the ESP_05 0x106 and Motor_20 0x121 CAN messages,
|
||||
respectively. A rising edge of either signals triggers a disengagement and is enforced by openpilot.
|
||||
The cancellation due to the rising edge of the gas pressed signal is also enforced by the Panda firmware.
|
||||
Additionally, the cruise control system disengages on the rising edge of the brake pedal pressed signal,
|
||||
and it's enforced by both openpilot and the Panda firmware.
|
||||
|
||||
**Extra note**: comma.ai strongly discourages the use of openpilot forks with safety code either missing or
|
||||
not fully meeting the above requirements.
|
||||
@@ -1,104 +0,0 @@
|
||||
Metadata-Version: 1.1
|
||||
Name: Werkzeug
|
||||
Version: 0.14.1
|
||||
Summary: The comprehensive WSGI web application library.
|
||||
Home-page: https://www.palletsprojects.org/p/werkzeug/
|
||||
Author: Armin Ronacher
|
||||
Author-email: armin.ronacher@active-4.com
|
||||
License: BSD
|
||||
Description: Werkzeug
|
||||
========
|
||||
|
||||
Werkzeug is a comprehensive `WSGI`_ web application library. It began as
|
||||
a simple collection of various utilities for WSGI applications and has
|
||||
become one of the most advanced WSGI utility libraries.
|
||||
|
||||
It includes:
|
||||
|
||||
* An interactive debugger that allows inspecting stack traces and source
|
||||
code in the browser with an interactive interpreter for any frame in
|
||||
the stack.
|
||||
* A full-featured request object with objects to interact with headers,
|
||||
query args, form data, files, and cookies.
|
||||
* A response object that can wrap other WSGI applications and handle
|
||||
streaming data.
|
||||
* A routing system for matching URLs to endpoints and generating URLs
|
||||
for endpoints, with an extensible system for capturing variables from
|
||||
URLs.
|
||||
* HTTP utilities to handle entity tags, cache control, dates, user
|
||||
agents, cookies, files, and more.
|
||||
* A threaded WSGI server for use while developing applications locally.
|
||||
* A test client for simulating HTTP requests during testing without
|
||||
requiring running a server.
|
||||
|
||||
Werkzeug is Unicode aware and doesn't enforce any dependencies. It is up
|
||||
to the developer to choose a template engine, database adapter, and even
|
||||
how to handle requests. It can be used to build all sorts of end user
|
||||
applications such as blogs, wikis, or bulletin boards.
|
||||
|
||||
`Flask`_ wraps Werkzeug, using it to handle the details of WSGI while
|
||||
providing more structure and patterns for defining powerful
|
||||
applications.
|
||||
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
Install and update using `pip`_:
|
||||
|
||||
.. code-block:: text
|
||||
|
||||
pip install -U Werkzeug
|
||||
|
||||
|
||||
A Simple Example
|
||||
----------------
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from werkzeug.wrappers import Request, Response
|
||||
|
||||
@Request.application
|
||||
def application(request):
|
||||
return Response('Hello, World!')
|
||||
|
||||
if __name__ == '__main__':
|
||||
from werkzeug.serving import run_simple
|
||||
run_simple('localhost', 4000, application)
|
||||
|
||||
|
||||
Links
|
||||
-----
|
||||
|
||||
* Website: https://www.palletsprojects.com/p/werkzeug/
|
||||
* Releases: https://pypi.org/project/Werkzeug/
|
||||
* Code: https://github.com/pallets/werkzeug
|
||||
* Issue tracker: https://github.com/pallets/werkzeug/issues
|
||||
* Test status:
|
||||
|
||||
* Linux, Mac: https://travis-ci.org/pallets/werkzeug
|
||||
* Windows: https://ci.appveyor.com/project/davidism/werkzeug
|
||||
|
||||
* Test coverage: https://codecov.io/gh/pallets/werkzeug
|
||||
|
||||
.. _WSGI: https://wsgi.readthedocs.io/en/latest/
|
||||
.. _Flask: https://www.palletsprojects.com/p/flask/
|
||||
.. _pip: https://pip.pypa.io/en/stable/quickstart/
|
||||
|
||||
Platform: any
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Environment :: Web Environment
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.6
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
||||
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
||||
@@ -1,299 +0,0 @@
|
||||
AUTHORS
|
||||
CHANGES.rst
|
||||
LICENSE
|
||||
MANIFEST.in
|
||||
Makefile
|
||||
README.rst
|
||||
setup.cfg
|
||||
setup.py
|
||||
tox.ini
|
||||
Werkzeug.egg-info/PKG-INFO
|
||||
Werkzeug.egg-info/SOURCES.txt
|
||||
Werkzeug.egg-info/dependency_links.txt
|
||||
Werkzeug.egg-info/not-zip-safe
|
||||
Werkzeug.egg-info/requires.txt
|
||||
Werkzeug.egg-info/top_level.txt
|
||||
artwork/.DS_Store
|
||||
artwork/logo.png
|
||||
artwork/logo.svg
|
||||
docs/.DS_Store
|
||||
docs/Makefile
|
||||
docs/changes.rst
|
||||
docs/conf.py
|
||||
docs/contents.rst.inc
|
||||
docs/datastructures.rst
|
||||
docs/debug.rst
|
||||
docs/exceptions.rst
|
||||
docs/filesystem.rst
|
||||
docs/http.rst
|
||||
docs/index.rst
|
||||
docs/installation.rst
|
||||
docs/latexindex.rst
|
||||
docs/levels.rst
|
||||
docs/local.rst
|
||||
docs/logo.pdf
|
||||
docs/make.bat
|
||||
docs/makearchive.py
|
||||
docs/middlewares.rst
|
||||
docs/python3.rst
|
||||
docs/quickstart.rst
|
||||
docs/request_data.rst
|
||||
docs/routing.rst
|
||||
docs/serving.rst
|
||||
docs/terms.rst
|
||||
docs/test.rst
|
||||
docs/transition.rst
|
||||
docs/tutorial.rst
|
||||
docs/unicode.rst
|
||||
docs/urls.rst
|
||||
docs/utils.rst
|
||||
docs/werkzeugext.py
|
||||
docs/werkzeugstyle.sty
|
||||
docs/wrappers.rst
|
||||
docs/wsgi.rst
|
||||
docs/_static/background.png
|
||||
docs/_static/codebackground.png
|
||||
docs/_static/contents.png
|
||||
docs/_static/debug-screenshot.png
|
||||
docs/_static/favicon.ico
|
||||
docs/_static/header.png
|
||||
docs/_static/navigation.png
|
||||
docs/_static/navigation_active.png
|
||||
docs/_static/shortly.png
|
||||
docs/_static/shorty-screenshot.png
|
||||
docs/_static/style.css
|
||||
docs/_static/werkzeug.js
|
||||
docs/_static/werkzeug.png
|
||||
docs/_templates/sidebarintro.html
|
||||
docs/_templates/sidebarlogo.html
|
||||
docs/contrib/atom.rst
|
||||
docs/contrib/cache.rst
|
||||
docs/contrib/fixers.rst
|
||||
docs/contrib/index.rst
|
||||
docs/contrib/iterio.rst
|
||||
docs/contrib/lint.rst
|
||||
docs/contrib/profiler.rst
|
||||
docs/contrib/securecookie.rst
|
||||
docs/contrib/sessions.rst
|
||||
docs/contrib/wrappers.rst
|
||||
docs/deployment/cgi.rst
|
||||
docs/deployment/fastcgi.rst
|
||||
docs/deployment/index.rst
|
||||
docs/deployment/mod_wsgi.rst
|
||||
docs/deployment/proxying.rst
|
||||
examples/README
|
||||
examples/cookieauth.py
|
||||
examples/httpbasicauth.py
|
||||
examples/manage-coolmagic.py
|
||||
examples/manage-couchy.py
|
||||
examples/manage-cupoftee.py
|
||||
examples/manage-i18nurls.py
|
||||
examples/manage-plnt.py
|
||||
examples/manage-shorty.py
|
||||
examples/manage-simplewiki.py
|
||||
examples/manage-webpylike.py
|
||||
examples/upload.py
|
||||
examples/contrib/README
|
||||
examples/contrib/securecookie.py
|
||||
examples/contrib/sessions.py
|
||||
examples/coolmagic/__init__.py
|
||||
examples/coolmagic/application.py
|
||||
examples/coolmagic/helpers.py
|
||||
examples/coolmagic/utils.py
|
||||
examples/coolmagic/public/style.css
|
||||
examples/coolmagic/templates/layout.html
|
||||
examples/coolmagic/templates/static/about.html
|
||||
examples/coolmagic/templates/static/index.html
|
||||
examples/coolmagic/templates/static/not_found.html
|
||||
examples/coolmagic/views/__init__.py
|
||||
examples/coolmagic/views/static.py
|
||||
examples/couchy/README
|
||||
examples/couchy/__init__.py
|
||||
examples/couchy/application.py
|
||||
examples/couchy/models.py
|
||||
examples/couchy/utils.py
|
||||
examples/couchy/views.py
|
||||
examples/couchy/static/style.css
|
||||
examples/couchy/templates/display.html
|
||||
examples/couchy/templates/layout.html
|
||||
examples/couchy/templates/list.html
|
||||
examples/couchy/templates/new.html
|
||||
examples/couchy/templates/not_found.html
|
||||
examples/cupoftee/__init__.py
|
||||
examples/cupoftee/application.py
|
||||
examples/cupoftee/db.py
|
||||
examples/cupoftee/network.py
|
||||
examples/cupoftee/pages.py
|
||||
examples/cupoftee/utils.py
|
||||
examples/cupoftee/shared/content.png
|
||||
examples/cupoftee/shared/down.png
|
||||
examples/cupoftee/shared/favicon.ico
|
||||
examples/cupoftee/shared/header.png
|
||||
examples/cupoftee/shared/logo.png
|
||||
examples/cupoftee/shared/style.css
|
||||
examples/cupoftee/shared/up.png
|
||||
examples/cupoftee/templates/layout.html
|
||||
examples/cupoftee/templates/missingpage.html
|
||||
examples/cupoftee/templates/search.html
|
||||
examples/cupoftee/templates/server.html
|
||||
examples/cupoftee/templates/serverlist.html
|
||||
examples/i18nurls/__init__.py
|
||||
examples/i18nurls/application.py
|
||||
examples/i18nurls/urls.py
|
||||
examples/i18nurls/views.py
|
||||
examples/i18nurls/templates/about.html
|
||||
examples/i18nurls/templates/blog.html
|
||||
examples/i18nurls/templates/index.html
|
||||
examples/i18nurls/templates/layout.html
|
||||
examples/partial/README
|
||||
examples/partial/complex_routing.py
|
||||
examples/plnt/__init__.py
|
||||
examples/plnt/database.py
|
||||
examples/plnt/sync.py
|
||||
examples/plnt/utils.py
|
||||
examples/plnt/views.py
|
||||
examples/plnt/webapp.py
|
||||
examples/plnt/shared/style.css
|
||||
examples/plnt/templates/about.html
|
||||
examples/plnt/templates/index.html
|
||||
examples/plnt/templates/layout.html
|
||||
examples/shortly/shortly.py
|
||||
examples/shortly/static/style.css
|
||||
examples/shortly/templates/404.html
|
||||
examples/shortly/templates/layout.html
|
||||
examples/shortly/templates/new_url.html
|
||||
examples/shortly/templates/short_link_details.html
|
||||
examples/shorty/__init__.py
|
||||
examples/shorty/application.py
|
||||
examples/shorty/models.py
|
||||
examples/shorty/utils.py
|
||||
examples/shorty/views.py
|
||||
examples/shorty/static/style.css
|
||||
examples/shorty/templates/display.html
|
||||
examples/shorty/templates/layout.html
|
||||
examples/shorty/templates/list.html
|
||||
examples/shorty/templates/new.html
|
||||
examples/shorty/templates/not_found.html
|
||||
examples/simplewiki/__init__.py
|
||||
examples/simplewiki/actions.py
|
||||
examples/simplewiki/application.py
|
||||
examples/simplewiki/database.py
|
||||
examples/simplewiki/specialpages.py
|
||||
examples/simplewiki/utils.py
|
||||
examples/simplewiki/shared/style.css
|
||||
examples/simplewiki/templates/action_diff.html
|
||||
examples/simplewiki/templates/action_edit.html
|
||||
examples/simplewiki/templates/action_log.html
|
||||
examples/simplewiki/templates/action_revert.html
|
||||
examples/simplewiki/templates/action_show.html
|
||||
examples/simplewiki/templates/layout.html
|
||||
examples/simplewiki/templates/macros.xml
|
||||
examples/simplewiki/templates/missing_action.html
|
||||
examples/simplewiki/templates/page_index.html
|
||||
examples/simplewiki/templates/page_missing.html
|
||||
examples/simplewiki/templates/recent_changes.html
|
||||
examples/webpylike/example.py
|
||||
examples/webpylike/webpylike.py
|
||||
tests/__init__.py
|
||||
tests/conftest.py
|
||||
tests/test_compat.py
|
||||
tests/test_datastructures.py
|
||||
tests/test_debug.py
|
||||
tests/test_exceptions.py
|
||||
tests/test_formparser.py
|
||||
tests/test_http.py
|
||||
tests/test_internal.py
|
||||
tests/test_local.py
|
||||
tests/test_routing.py
|
||||
tests/test_security.py
|
||||
tests/test_serving.py
|
||||
tests/test_test.py
|
||||
tests/test_urls.py
|
||||
tests/test_utils.py
|
||||
tests/test_wrappers.py
|
||||
tests/test_wsgi.py
|
||||
tests/contrib/__init__.py
|
||||
tests/contrib/test_atom.py
|
||||
tests/contrib/test_cache.py
|
||||
tests/contrib/test_fixers.py
|
||||
tests/contrib/test_iterio.py
|
||||
tests/contrib/test_securecookie.py
|
||||
tests/contrib/test_sessions.py
|
||||
tests/contrib/test_wrappers.py
|
||||
tests/contrib/cache/conftest.py
|
||||
tests/contrib/cache/test_cache.py
|
||||
tests/hypothesis/__init__.py
|
||||
tests/hypothesis/test_urls.py
|
||||
tests/multipart/__init__.py
|
||||
tests/multipart/ie7_full_path_request.txt
|
||||
tests/multipart/test_collect.py
|
||||
tests/multipart/firefox3-2png1txt/file1.png
|
||||
tests/multipart/firefox3-2png1txt/file2.png
|
||||
tests/multipart/firefox3-2png1txt/request.txt
|
||||
tests/multipart/firefox3-2png1txt/text.txt
|
||||
tests/multipart/firefox3-2pnglongtext/file1.png
|
||||
tests/multipart/firefox3-2pnglongtext/file2.png
|
||||
tests/multipart/firefox3-2pnglongtext/request.txt
|
||||
tests/multipart/firefox3-2pnglongtext/text.txt
|
||||
tests/multipart/ie6-2png1txt/file1.png
|
||||
tests/multipart/ie6-2png1txt/file2.png
|
||||
tests/multipart/ie6-2png1txt/request.txt
|
||||
tests/multipart/ie6-2png1txt/text.txt
|
||||
tests/multipart/opera8-2png1txt/file1.png
|
||||
tests/multipart/opera8-2png1txt/file2.png
|
||||
tests/multipart/opera8-2png1txt/request.txt
|
||||
tests/multipart/opera8-2png1txt/text.txt
|
||||
tests/multipart/webkit3-2png1txt/file1.png
|
||||
tests/multipart/webkit3-2png1txt/file2.png
|
||||
tests/multipart/webkit3-2png1txt/request.txt
|
||||
tests/multipart/webkit3-2png1txt/text.txt
|
||||
tests/res/chunked.txt
|
||||
tests/res/test.txt
|
||||
werkzeug/__init__.py
|
||||
werkzeug/_compat.py
|
||||
werkzeug/_internal.py
|
||||
werkzeug/_reloader.py
|
||||
werkzeug/datastructures.py
|
||||
werkzeug/exceptions.py
|
||||
werkzeug/filesystem.py
|
||||
werkzeug/formparser.py
|
||||
werkzeug/http.py
|
||||
werkzeug/local.py
|
||||
werkzeug/posixemulation.py
|
||||
werkzeug/routing.py
|
||||
werkzeug/security.py
|
||||
werkzeug/serving.py
|
||||
werkzeug/test.py
|
||||
werkzeug/testapp.py
|
||||
werkzeug/urls.py
|
||||
werkzeug/useragents.py
|
||||
werkzeug/utils.py
|
||||
werkzeug/websocket.py
|
||||
werkzeug/wrappers.py
|
||||
werkzeug/wsgi.py
|
||||
werkzeug/contrib/__init__.py
|
||||
werkzeug/contrib/atom.py
|
||||
werkzeug/contrib/cache.py
|
||||
werkzeug/contrib/fixers.py
|
||||
werkzeug/contrib/iterio.py
|
||||
werkzeug/contrib/jsrouting.py
|
||||
werkzeug/contrib/limiter.py
|
||||
werkzeug/contrib/lint.py
|
||||
werkzeug/contrib/profiler.py
|
||||
werkzeug/contrib/securecookie.py
|
||||
werkzeug/contrib/sessions.py
|
||||
werkzeug/contrib/testtools.py
|
||||
werkzeug/contrib/wrappers.py
|
||||
werkzeug/debug/__init__.py
|
||||
werkzeug/debug/console.py
|
||||
werkzeug/debug/repr.py
|
||||
werkzeug/debug/tbtools.py
|
||||
werkzeug/debug/shared/FONT_LICENSE
|
||||
werkzeug/debug/shared/console.png
|
||||
werkzeug/debug/shared/debugger.js
|
||||
werkzeug/debug/shared/jquery.js
|
||||
werkzeug/debug/shared/less.png
|
||||
werkzeug/debug/shared/more.png
|
||||
werkzeug/debug/shared/source.png
|
||||
werkzeug/debug/shared/style.css
|
||||
werkzeug/debug/shared/ubuntu.ttf
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
../werkzeug/_reloader.py
|
||||
../werkzeug/_internal.py
|
||||
../werkzeug/serving.py
|
||||
../werkzeug/local.py
|
||||
../werkzeug/filesystem.py
|
||||
../werkzeug/security.py
|
||||
../werkzeug/__init__.py
|
||||
../werkzeug/test.py
|
||||
../werkzeug/formparser.py
|
||||
../werkzeug/posixemulation.py
|
||||
../werkzeug/utils.py
|
||||
../werkzeug/wrappers.py
|
||||
../werkzeug/routing.py
|
||||
../werkzeug/http.py
|
||||
../werkzeug/useragents.py
|
||||
../werkzeug/exceptions.py
|
||||
../werkzeug/_compat.py
|
||||
../werkzeug/datastructures.py
|
||||
../werkzeug/urls.py
|
||||
../werkzeug/websocket.py
|
||||
../werkzeug/wsgi.py
|
||||
../werkzeug/testapp.py
|
||||
../werkzeug/contrib/sessions.py
|
||||
../werkzeug/contrib/cache.py
|
||||
../werkzeug/contrib/__init__.py
|
||||
../werkzeug/contrib/testtools.py
|
||||
../werkzeug/contrib/wrappers.py
|
||||
../werkzeug/contrib/jsrouting.py
|
||||
../werkzeug/contrib/fixers.py
|
||||
../werkzeug/contrib/profiler.py
|
||||
../werkzeug/contrib/iterio.py
|
||||
../werkzeug/contrib/atom.py
|
||||
../werkzeug/contrib/securecookie.py
|
||||
../werkzeug/contrib/limiter.py
|
||||
../werkzeug/contrib/lint.py
|
||||
../werkzeug/debug/console.py
|
||||
../werkzeug/debug/tbtools.py
|
||||
../werkzeug/debug/__init__.py
|
||||
../werkzeug/debug/repr.py
|
||||
../werkzeug/debug/shared/FONT_LICENSE
|
||||
../werkzeug/debug/shared/console.png
|
||||
../werkzeug/debug/shared/debugger.js
|
||||
../werkzeug/debug/shared/jquery.js
|
||||
../werkzeug/debug/shared/less.png
|
||||
../werkzeug/debug/shared/more.png
|
||||
../werkzeug/debug/shared/source.png
|
||||
../werkzeug/debug/shared/style.css
|
||||
../werkzeug/debug/shared/ubuntu.ttf
|
||||
../werkzeug/_reloader.pyc
|
||||
../werkzeug/_internal.pyc
|
||||
../werkzeug/serving.pyc
|
||||
../werkzeug/local.pyc
|
||||
../werkzeug/filesystem.pyc
|
||||
../werkzeug/security.pyc
|
||||
../werkzeug/__init__.pyc
|
||||
../werkzeug/test.pyc
|
||||
../werkzeug/formparser.pyc
|
||||
../werkzeug/posixemulation.pyc
|
||||
../werkzeug/utils.pyc
|
||||
../werkzeug/wrappers.pyc
|
||||
../werkzeug/routing.pyc
|
||||
../werkzeug/http.pyc
|
||||
../werkzeug/useragents.pyc
|
||||
../werkzeug/exceptions.pyc
|
||||
../werkzeug/_compat.pyc
|
||||
../werkzeug/datastructures.pyc
|
||||
../werkzeug/urls.pyc
|
||||
../werkzeug/websocket.pyc
|
||||
../werkzeug/wsgi.pyc
|
||||
../werkzeug/testapp.pyc
|
||||
../werkzeug/contrib/sessions.pyc
|
||||
../werkzeug/contrib/cache.pyc
|
||||
../werkzeug/contrib/__init__.pyc
|
||||
../werkzeug/contrib/testtools.pyc
|
||||
../werkzeug/contrib/wrappers.pyc
|
||||
../werkzeug/contrib/jsrouting.pyc
|
||||
../werkzeug/contrib/fixers.pyc
|
||||
../werkzeug/contrib/profiler.pyc
|
||||
../werkzeug/contrib/iterio.pyc
|
||||
../werkzeug/contrib/atom.pyc
|
||||
../werkzeug/contrib/securecookie.pyc
|
||||
../werkzeug/contrib/limiter.pyc
|
||||
../werkzeug/contrib/lint.pyc
|
||||
../werkzeug/debug/console.pyc
|
||||
../werkzeug/debug/tbtools.pyc
|
||||
../werkzeug/debug/__init__.pyc
|
||||
../werkzeug/debug/repr.pyc
|
||||
PKG-INFO
|
||||
not-zip-safe
|
||||
SOURCES.txt
|
||||
requires.txt
|
||||
top_level.txt
|
||||
dependency_links.txt
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
|
||||
[dev]
|
||||
pytest
|
||||
coverage
|
||||
tox
|
||||
sphinx
|
||||
|
||||
[termcolor]
|
||||
termcolor
|
||||
|
||||
[watchdog]
|
||||
watchdog
|
||||
@@ -1 +0,0 @@
|
||||
werkzeug
|
||||
BIN
apk/0.6.6-en/ai.comma.plus.black.apk
Normal file
BIN
apk/0.6.6-en/ai.comma.plus.black.apk
Normal file
Binary file not shown.
BIN
apk/0.6.6-en/ai.comma.plus.frame.apk
Normal file
BIN
apk/0.6.6-en/ai.comma.plus.frame.apk
Normal file
Binary file not shown.
BIN
apk/0.6.6-en/ai.comma.plus.offroad.apk
Normal file
BIN
apk/0.6.6-en/ai.comma.plus.offroad.apk
Normal file
Binary file not shown.
BIN
apk/0.6.6-en/cn.dragonpilot.gpsservice.apk
Normal file
BIN
apk/0.6.6-en/cn.dragonpilot.gpsservice.apk
Normal file
Binary file not shown.
BIN
apk/0.6.6-en/com.autonavi.amapauto.apk
Normal file
BIN
apk/0.6.6-en/com.autonavi.amapauto.apk
Normal file
Binary file not shown.
BIN
apk/0.6.6-en/com.mixplorer.apk
Normal file
BIN
apk/0.6.6-en/com.mixplorer.apk
Normal file
Binary file not shown.
BIN
apk/0.6.6-en/com.tomtom.speedcams.android.map.apk
Normal file
BIN
apk/0.6.6-en/com.tomtom.speedcams.android.map.apk
Normal file
Binary file not shown.
BIN
apk/0.6.6-en/tw.com.ainvest.outpack.apk
Normal file
BIN
apk/0.6.6-en/tw.com.ainvest.outpack.apk
Normal file
Binary file not shown.
BIN
apk/ai.comma.plus.black.apk
Normal file
BIN
apk/ai.comma.plus.black.apk
Normal file
Binary file not shown.
BIN
apk/ai.comma.plus.frame.apk
Normal file
BIN
apk/ai.comma.plus.frame.apk
Normal file
Binary file not shown.
BIN
apk/ai.comma.plus.offroad.apk
Normal file
BIN
apk/ai.comma.plus.offroad.apk
Normal file
Binary file not shown.
BIN
apk/cn.dragonpilot.gpsservice.apk
Normal file
BIN
apk/cn.dragonpilot.gpsservice.apk
Normal file
Binary file not shown.
BIN
apk/com.autonavi.amapauto.apk
Normal file
BIN
apk/com.autonavi.amapauto.apk
Normal file
Binary file not shown.
BIN
apk/com.mixplorer.apk
Normal file
BIN
apk/com.mixplorer.apk
Normal file
Binary file not shown.
BIN
apk/com.tomtom.speedcams.android.map.apk
Normal file
BIN
apk/com.tomtom.speedcams.android.map.apk
Normal file
Binary file not shown.
BIN
apk/tw.com.ainvest.outpack.apk
Normal file
BIN
apk/tw.com.ainvest.outpack.apk
Normal file
Binary file not shown.
10
bin/flask
10
bin/flask
@@ -1,10 +0,0 @@
|
||||
#!/usr/local/bin/python
|
||||
# EASY-INSTALL-ENTRY-SCRIPT: 'Flask==1.0.2','console_scripts','flask'
|
||||
__requires__ = 'Flask==1.0.2'
|
||||
import sys
|
||||
from pkg_resources import load_entry_point
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(
|
||||
load_entry_point('Flask==1.0.2', 'console_scripts', 'flask')()
|
||||
)
|
||||
6
cereal/.gitignore
vendored
Normal file
6
cereal/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
gen
|
||||
node_modules
|
||||
package-lock.json
|
||||
*.pyc
|
||||
__pycache__
|
||||
|
||||
62
cereal/Makefile
Normal file
62
cereal/Makefile
Normal file
@@ -0,0 +1,62 @@
|
||||
PWD := $(shell pwd)
|
||||
|
||||
SRCS := log.capnp car.capnp
|
||||
|
||||
GENS := gen/cpp/car.capnp.c++ gen/cpp/log.capnp.c++
|
||||
JS := gen/js/car.capnp.js gen/js/log.capnp.js
|
||||
|
||||
UNAME_M ?= $(shell uname -m)
|
||||
|
||||
GENS += gen/c/car.capnp.c gen/c/log.capnp.c gen/c/include/c++.capnp.h gen/c/include/java.capnp.h
|
||||
|
||||
ifeq ($(UNAME_M),x86_64)
|
||||
|
||||
ifneq (, $(shell which capnpc-java))
|
||||
GENS += gen/java/Car.java gen/java/Log.java
|
||||
else
|
||||
$(warning capnpc-java not found, skipping java build)
|
||||
endif
|
||||
|
||||
endif
|
||||
|
||||
|
||||
ifeq ($(UNAME_M),aarch64)
|
||||
CAPNPC=PATH=$(PWD)/../phonelibs/capnp-cpp/aarch64/bin/:$$PATH capnpc
|
||||
else
|
||||
CAPNPC=capnpc
|
||||
endif
|
||||
|
||||
.PHONY: all
|
||||
all: $(GENS)
|
||||
js: $(JS)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -rf gen
|
||||
rm -rf node_modules
|
||||
rm -rf package-lock.json
|
||||
|
||||
gen/c/%.capnp.c: %.capnp
|
||||
@echo "[ CAPNPC C ] $@"
|
||||
mkdir -p gen/c/
|
||||
$(CAPNPC) '$<' -o c:gen/c/
|
||||
|
||||
gen/js/%.capnp.js: %.capnp
|
||||
@echo "[ CAPNPC JavaScript ] $@"
|
||||
mkdir -p gen/js/
|
||||
sh ./generate_javascript.sh
|
||||
|
||||
gen/cpp/%.capnp.c++: %.capnp
|
||||
@echo "[ CAPNPC C++ ] $@"
|
||||
mkdir -p gen/cpp/
|
||||
$(CAPNPC) '$<' -o c++:gen/cpp/
|
||||
|
||||
gen/java/Car.java gen/java/Log.java: $(SRCS)
|
||||
@echo "[ CAPNPC java ] $@"
|
||||
mkdir -p gen/java/
|
||||
$(CAPNPC) $^ -o java:gen/java
|
||||
|
||||
# c-capnproto needs some empty headers
|
||||
gen/c/include/c++.capnp.h gen/c/include/java.capnp.h:
|
||||
mkdir -p gen/c/include
|
||||
touch '$@'
|
||||
8
cereal/__init__.py
Normal file
8
cereal/__init__.py
Normal file
@@ -0,0 +1,8 @@
|
||||
import os
|
||||
import capnp
|
||||
|
||||
CEREAL_PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
capnp.remove_import_hook()
|
||||
|
||||
log = capnp.load(os.path.join(CEREAL_PATH, "log.capnp"))
|
||||
car = capnp.load(os.path.join(CEREAL_PATH, "car.capnp"))
|
||||
439
cereal/car.capnp
Normal file
439
cereal/car.capnp
Normal file
@@ -0,0 +1,439 @@
|
||||
using Cxx = import "./include/c++.capnp";
|
||||
$Cxx.namespace("cereal");
|
||||
|
||||
using Java = import "./include/java.capnp";
|
||||
$Java.package("ai.comma.openpilot.cereal");
|
||||
$Java.outerClassname("Car");
|
||||
|
||||
@0x8e2af1e708af8b8d;
|
||||
|
||||
# ******* events causing controls state machine transition *******
|
||||
|
||||
struct CarEvent @0x9b1657f34caf3ad3 {
|
||||
name @0 :EventName;
|
||||
enable @1 :Bool;
|
||||
noEntry @2 :Bool;
|
||||
warning @3 :Bool;
|
||||
userDisable @4 :Bool;
|
||||
softDisable @5 :Bool;
|
||||
immediateDisable @6 :Bool;
|
||||
preEnable @7 :Bool;
|
||||
permanent @8 :Bool;
|
||||
|
||||
enum EventName @0xbaa8c5d505f727de {
|
||||
# TODO: copy from error list
|
||||
canError @0;
|
||||
steerUnavailable @1;
|
||||
brakeUnavailable @2;
|
||||
gasUnavailable @3;
|
||||
wrongGear @4;
|
||||
doorOpen @5;
|
||||
seatbeltNotLatched @6;
|
||||
espDisabled @7;
|
||||
wrongCarMode @8;
|
||||
steerTempUnavailable @9;
|
||||
reverseGear @10;
|
||||
buttonCancel @11;
|
||||
buttonEnable @12;
|
||||
pedalPressed @13;
|
||||
cruiseDisabled @14;
|
||||
radarCanError @15;
|
||||
dataNeeded @16;
|
||||
speedTooLow @17;
|
||||
outOfSpace @18;
|
||||
overheat @19;
|
||||
calibrationIncomplete @20;
|
||||
calibrationInvalid @21;
|
||||
controlsMismatch @22;
|
||||
pcmEnable @23;
|
||||
pcmDisable @24;
|
||||
noTarget @25;
|
||||
radarFault @26;
|
||||
modelCommIssueDEPRECATED @27;
|
||||
brakeHold @28;
|
||||
parkBrake @29;
|
||||
manualRestart @30;
|
||||
lowSpeedLockout @31;
|
||||
plannerError @32;
|
||||
ipasOverride @33;
|
||||
debugAlert @34;
|
||||
steerTempUnavailableMute @35;
|
||||
resumeRequired @36;
|
||||
preDriverDistracted @37;
|
||||
promptDriverDistracted @38;
|
||||
driverDistracted @39;
|
||||
geofence @40;
|
||||
driverMonitorOn @41;
|
||||
driverMonitorOff @42;
|
||||
preDriverUnresponsive @43;
|
||||
promptDriverUnresponsive @44;
|
||||
driverUnresponsive @45;
|
||||
belowSteerSpeed @46;
|
||||
calibrationProgress @47;
|
||||
lowBattery @48;
|
||||
invalidGiraffeHonda @49;
|
||||
vehicleModelInvalid @50;
|
||||
controlsFailed @51;
|
||||
sensorDataInvalid @52;
|
||||
commIssue @53;
|
||||
tooDistracted @54;
|
||||
posenetInvalid @55;
|
||||
soundsUnavailable @56;
|
||||
preLaneChangeLeft @57;
|
||||
preLaneChangeRight @58;
|
||||
laneChange @59;
|
||||
invalidGiraffeToyota @60;
|
||||
internetConnectivityNeeded @61;
|
||||
manualSteeringRequired @62;
|
||||
manualSteeringRequiredBlinkersOn @63;
|
||||
leadCarMoving @64;
|
||||
leadCarDetected @65;
|
||||
}
|
||||
}
|
||||
|
||||
# ******* main car state @ 100hz *******
|
||||
# all speeds in m/s
|
||||
|
||||
struct CarState {
|
||||
errorsDEPRECATED @0 :List(CarEvent.EventName);
|
||||
events @13 :List(CarEvent);
|
||||
|
||||
# car speed
|
||||
vEgo @1 :Float32; # best estimate of speed
|
||||
aEgo @16 :Float32; # best estimate of acceleration
|
||||
vEgoRaw @17 :Float32; # unfiltered speed from CAN sensors
|
||||
yawRate @22 :Float32; # best estimate of yaw rate
|
||||
standstill @18 :Bool;
|
||||
wheelSpeeds @2 :WheelSpeeds;
|
||||
|
||||
# gas pedal, 0.0-1.0
|
||||
gas @3 :Float32; # this is user + computer
|
||||
gasPressed @4 :Bool; # this is user pedal only
|
||||
|
||||
# brake pedal, 0.0-1.0
|
||||
brake @5 :Float32; # this is user pedal only
|
||||
brakePressed @6 :Bool; # this is user pedal only
|
||||
brakeLights @19 :Bool;
|
||||
|
||||
# steering wheel
|
||||
steeringAngle @7 :Float32; # deg
|
||||
steeringRate @15 :Float32; # deg/s
|
||||
steeringTorque @8 :Float32; # TODO: standardize units
|
||||
steeringTorqueEps @27 :Float32; # TODO: standardize units
|
||||
steeringPressed @9 :Bool; # if the user is using the steering wheel
|
||||
|
||||
# cruise state
|
||||
cruiseState @10 :CruiseState;
|
||||
|
||||
# gear
|
||||
gearShifter @14 :GearShifter;
|
||||
|
||||
# button presses
|
||||
buttonEvents @11 :List(ButtonEvent);
|
||||
leftBlinker @20 :Bool;
|
||||
rightBlinker @21 :Bool;
|
||||
genericToggle @23 :Bool;
|
||||
|
||||
# lock info
|
||||
doorOpen @24 :Bool;
|
||||
seatbeltUnlatched @25 :Bool;
|
||||
canValid @26 :Bool;
|
||||
|
||||
# clutch (manual transmission only)
|
||||
clutchPressed @28 :Bool;
|
||||
|
||||
# which packets this state came from
|
||||
canMonoTimes @12: List(UInt64);
|
||||
|
||||
struct WheelSpeeds {
|
||||
# optional wheel speeds
|
||||
fl @0 :Float32;
|
||||
fr @1 :Float32;
|
||||
rl @2 :Float32;
|
||||
rr @3 :Float32;
|
||||
}
|
||||
|
||||
struct CruiseState {
|
||||
enabled @0 :Bool;
|
||||
speed @1 :Float32;
|
||||
available @2 :Bool;
|
||||
speedOffset @3 :Float32;
|
||||
standstill @4 :Bool;
|
||||
}
|
||||
|
||||
enum GearShifter {
|
||||
unknown @0;
|
||||
park @1;
|
||||
drive @2;
|
||||
neutral @3;
|
||||
reverse @4;
|
||||
sport @5;
|
||||
low @6;
|
||||
brake @7;
|
||||
eco @8;
|
||||
manumatic @9;
|
||||
}
|
||||
|
||||
|
||||
# send on change
|
||||
struct ButtonEvent {
|
||||
pressed @0 :Bool;
|
||||
type @1 :Type;
|
||||
|
||||
enum Type {
|
||||
unknown @0;
|
||||
leftBlinker @1;
|
||||
rightBlinker @2;
|
||||
accelCruise @3;
|
||||
decelCruise @4;
|
||||
cancel @5;
|
||||
altButton1 @6;
|
||||
altButton2 @7;
|
||||
altButton3 @8;
|
||||
setCruise @9;
|
||||
resumeCruise @10;
|
||||
gapAdjustCruise @11;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ******* radar state @ 20hz *******
|
||||
|
||||
struct RadarData @0x888ad6581cf0aacb {
|
||||
errors @0 :List(Error);
|
||||
points @1 :List(RadarPoint);
|
||||
|
||||
# which packets this state came from
|
||||
canMonoTimes @2 :List(UInt64);
|
||||
|
||||
enum Error {
|
||||
canError @0;
|
||||
fault @1;
|
||||
wrongConfig @2;
|
||||
}
|
||||
|
||||
# similar to LiveTracks
|
||||
# is one timestamp valid for all? I think so
|
||||
struct RadarPoint {
|
||||
trackId @0 :UInt64; # no trackId reuse
|
||||
|
||||
# these 3 are the minimum required
|
||||
dRel @1 :Float32; # m from the front bumper of the car
|
||||
yRel @2 :Float32; # m
|
||||
vRel @3 :Float32; # m/s
|
||||
|
||||
# these are optional and valid if they are not NaN
|
||||
aRel @4 :Float32; # m/s^2
|
||||
yvRel @5 :Float32; # m/s
|
||||
|
||||
# some radars flag measurements VS estimates
|
||||
measured @6 :Bool;
|
||||
}
|
||||
}
|
||||
|
||||
# ******* car controls @ 100hz *******
|
||||
|
||||
struct CarControl {
|
||||
# must be true for any actuator commands to work
|
||||
enabled @0 :Bool;
|
||||
active @7 :Bool;
|
||||
|
||||
gasDEPRECATED @1 :Float32;
|
||||
brakeDEPRECATED @2 :Float32;
|
||||
steeringTorqueDEPRECATED @3 :Float32;
|
||||
|
||||
actuators @6 :Actuators;
|
||||
|
||||
cruiseControl @4 :CruiseControl;
|
||||
hudControl @5 :HUDControl;
|
||||
|
||||
struct Actuators {
|
||||
# range from 0.0 - 1.0
|
||||
gas @0: Float32;
|
||||
brake @1: Float32;
|
||||
# range from -1.0 - 1.0
|
||||
steer @2: Float32;
|
||||
steerAngle @3: Float32;
|
||||
}
|
||||
|
||||
struct CruiseControl {
|
||||
cancel @0: Bool;
|
||||
override @1: Bool;
|
||||
speedOverride @2: Float32;
|
||||
accelOverride @3: Float32;
|
||||
}
|
||||
|
||||
struct HUDControl {
|
||||
speedVisible @0: Bool;
|
||||
setSpeed @1: Float32;
|
||||
lanesVisible @2: Bool;
|
||||
leadVisible @3: Bool;
|
||||
visualAlert @4: VisualAlert;
|
||||
audibleAlert @5: AudibleAlert;
|
||||
rightLaneVisible @6: Bool;
|
||||
leftLaneVisible @7: Bool;
|
||||
rightLaneDepart @8: Bool;
|
||||
leftLaneDepart @9: Bool;
|
||||
|
||||
enum VisualAlert {
|
||||
# these are the choices from the Honda
|
||||
# map as good as you can for your car
|
||||
none @0;
|
||||
fcw @1;
|
||||
steerRequired @2;
|
||||
brakePressed @3;
|
||||
wrongGear @4;
|
||||
seatbeltUnbuckled @5;
|
||||
speedTooHigh @6;
|
||||
ldw @7;
|
||||
}
|
||||
|
||||
enum AudibleAlert {
|
||||
# these are the choices from the Honda
|
||||
# map as good as you can for your car
|
||||
none @0;
|
||||
chimeEngage @1;
|
||||
chimeDisengage @2;
|
||||
chimeError @3;
|
||||
chimeWarning1 @4;
|
||||
chimeWarning2 @5;
|
||||
chimeWarningRepeat @6;
|
||||
chimePrompt @7;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ****** car param ******
|
||||
|
||||
struct CarParams {
|
||||
carName @0 :Text;
|
||||
carFingerprint @1 :Text;
|
||||
|
||||
enableGasInterceptor @2 :Bool;
|
||||
enableCruise @3 :Bool;
|
||||
enableCamera @4 :Bool;
|
||||
enableDsu @5 :Bool; # driving support unit
|
||||
enableApgs @6 :Bool; # advanced parking guidance system
|
||||
|
||||
minEnableSpeed @7 :Float32;
|
||||
minSteerSpeed @8 :Float32;
|
||||
safetyModel @9 :SafetyModel;
|
||||
safetyModelPassive @42 :SafetyModel = noOutput;
|
||||
safetyParam @10 :Int16;
|
||||
|
||||
steerMaxBP @11 :List(Float32);
|
||||
steerMaxV @12 :List(Float32);
|
||||
gasMaxBP @13 :List(Float32);
|
||||
gasMaxV @14 :List(Float32);
|
||||
brakeMaxBP @15 :List(Float32);
|
||||
brakeMaxV @16 :List(Float32);
|
||||
|
||||
|
||||
# things about the car in the manual
|
||||
mass @17 :Float32; # [kg] running weight
|
||||
wheelbase @18 :Float32; # [m] distance from rear to front axle
|
||||
centerToFront @19 :Float32; # [m] GC distance to front axle
|
||||
steerRatio @20 :Float32; # [] ratio between front wheels and steering wheel angles
|
||||
steerRatioRear @21 :Float32; # [] rear steering ratio wrt front steering (usually 0)
|
||||
|
||||
# things we can derive
|
||||
rotationalInertia @22 :Float32; # [kg*m2] body rotational inertia
|
||||
tireStiffnessFront @23 :Float32; # [N/rad] front tire coeff of stiff
|
||||
tireStiffnessRear @24 :Float32; # [N/rad] rear tire coeff of stiff
|
||||
|
||||
longitudinalTuning @25 :LongitudinalPIDTuning;
|
||||
lateralTuning :union {
|
||||
pid @26 :LateralPIDTuning;
|
||||
indi @27 :LateralINDITuning;
|
||||
lqr @40 :LateralLQRTuning;
|
||||
}
|
||||
|
||||
steerLimitAlert @28 :Bool;
|
||||
|
||||
vEgoStopping @29 :Float32; # Speed at which the car goes into stopping state
|
||||
directAccelControl @30 :Bool; # Does the car have direct accel control or just gas/brake
|
||||
stoppingControl @31 :Bool; # Does the car allows full control even at lows speeds when stopping
|
||||
startAccel @32 :Float32; # Required acceleraton to overcome creep braking
|
||||
steerRateCost @33 :Float32; # Lateral MPC cost on steering rate
|
||||
steerControlType @34 :SteerControlType;
|
||||
radarOffCan @35 :Bool; # True when radar objects aren't visible on CAN
|
||||
|
||||
steerActuatorDelay @36 :Float32; # Steering wheel actuator delay in seconds
|
||||
openpilotLongitudinalControl @37 :Bool; # is openpilot doing the longitudinal control?
|
||||
carVin @38 :Text; # VIN number queried during fingerprinting
|
||||
isPandaBlack @39: Bool;
|
||||
dashcamOnly @41: Bool;
|
||||
transmissionType @43 :TransmissionType;
|
||||
|
||||
struct LateralPIDTuning {
|
||||
kpBP @0 :List(Float32);
|
||||
kpV @1 :List(Float32);
|
||||
kiBP @2 :List(Float32);
|
||||
kiV @3 :List(Float32);
|
||||
kf @4 :Float32;
|
||||
}
|
||||
|
||||
struct LongitudinalPIDTuning {
|
||||
kpBP @0 :List(Float32);
|
||||
kpV @1 :List(Float32);
|
||||
kiBP @2 :List(Float32);
|
||||
kiV @3 :List(Float32);
|
||||
deadzoneBP @4 :List(Float32);
|
||||
deadzoneV @5 :List(Float32);
|
||||
}
|
||||
|
||||
|
||||
struct LateralINDITuning {
|
||||
outerLoopGain @0 :Float32;
|
||||
innerLoopGain @1 :Float32;
|
||||
timeConstant @2 :Float32;
|
||||
actuatorEffectiveness @3 :Float32;
|
||||
}
|
||||
|
||||
struct LateralLQRTuning {
|
||||
scale @0 :Float32;
|
||||
ki @1 :Float32;
|
||||
dcGain @2 :Float32;
|
||||
|
||||
# State space system
|
||||
a @3 :List(Float32);
|
||||
b @4 :List(Float32);
|
||||
c @5 :List(Float32);
|
||||
|
||||
k @6 :List(Float32); # LQR gain
|
||||
l @7 :List(Float32); # Kalman gain
|
||||
}
|
||||
|
||||
enum SafetyModel {
|
||||
noOutput @0;
|
||||
honda @1;
|
||||
toyota @2;
|
||||
elm327 @3;
|
||||
gm @4;
|
||||
hondaBosch @5;
|
||||
ford @6;
|
||||
cadillac @7;
|
||||
hyundai @8;
|
||||
chrysler @9;
|
||||
tesla @10;
|
||||
subaru @11;
|
||||
gmPassive @12;
|
||||
mazda @13;
|
||||
nissan @14;
|
||||
volkswagen @15;
|
||||
toyotaIpas @16;
|
||||
allOutput @17;
|
||||
gmAscm @18;
|
||||
}
|
||||
|
||||
enum SteerControlType {
|
||||
torque @0;
|
||||
angle @1;
|
||||
}
|
||||
|
||||
enum TransmissionType {
|
||||
unknown @0;
|
||||
automatic @1;
|
||||
manual @2;
|
||||
}
|
||||
}
|
||||
26
cereal/generate_javascript.sh
Executable file
26
cereal/generate_javascript.sh
Executable file
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
|
||||
rm -r gen/ts
|
||||
rm -r gen/js
|
||||
|
||||
mkdir gen/ts
|
||||
mkdir gen/js
|
||||
|
||||
echo "Installing needed npm modules"
|
||||
npm i capnpc-ts capnp-ts
|
||||
|
||||
capnpc -o node_modules/.bin/capnpc-ts:gen/ts log.capnp car.capnp
|
||||
capnpc -o node_modules/.bin/capnpc-ts:gen/ts car.capnp
|
||||
|
||||
cat log.capnp | egrep '\([a-zA-Z]*\.[^\s]+\.[^s]+\)' | sed 's/^.*([a-zA-Z]*\.\([a-zA-Z.]*\)).*/\1/' | while read line
|
||||
do
|
||||
TOKEN=`echo $line | sed 's/\./_/g'`
|
||||
ROOT=`echo $line | sed 's/\..*$//g'`
|
||||
cat gen/ts/log.capnp.ts | grep '^import.*'${TOKEN}
|
||||
if [[ "$?" == "1" ]]
|
||||
then
|
||||
sed -i 's/^\(import {.*\)'${ROOT}'\(,*\) \(.*\)$/\1'${ROOT}', '${TOKEN}'\2 \3/' ./gen/ts/log.capnp.ts
|
||||
fi
|
||||
done
|
||||
|
||||
tsc ./gen/ts/* --lib es2015 --outDir ./gen/js
|
||||
26
cereal/include/c++.capnp
Normal file
26
cereal/include/c++.capnp
Normal file
@@ -0,0 +1,26 @@
|
||||
# Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
|
||||
# Licensed under the MIT License:
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
@0xbdf87d7bb8304e81;
|
||||
$namespace("capnp::annotations");
|
||||
|
||||
annotation namespace(file): Text;
|
||||
annotation name(field, enumerant, struct, enum, interface, method, param, group, union): Text;
|
||||
28
cereal/include/java.capnp
Normal file
28
cereal/include/java.capnp
Normal file
@@ -0,0 +1,28 @@
|
||||
# Copyright (c) 2013-2015 Sandstorm Development Group, Inc. and contributors
|
||||
# Licensed under the MIT License:
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
@0xc5f1af96651f70ea;
|
||||
|
||||
annotation package @0x9ee4c8f803b3b596 (file) : Text;
|
||||
# Name of the package, such as "org.example.foo", in which the generated code will reside.
|
||||
|
||||
annotation outerClassname @0x9b066bb4881f7cd3 (file) : Text;
|
||||
# Name of the outer class that will wrap the generated code.
|
||||
39
cereal/install_capnp.sh
Executable file
39
cereal/install_capnp.sh
Executable file
@@ -0,0 +1,39 @@
|
||||
set -e
|
||||
echo "Installing capnp"
|
||||
|
||||
cd /tmp
|
||||
VERSION=0.6.1
|
||||
wget https://capnproto.org/capnproto-c++-${VERSION}.tar.gz
|
||||
tar xvf capnproto-c++-${VERSION}.tar.gz
|
||||
cd capnproto-c++-${VERSION}
|
||||
CXXFLAGS="-fPIC" ./configure
|
||||
|
||||
make -j4
|
||||
|
||||
# manually build binaries statically
|
||||
g++ -std=gnu++11 -I./src -I./src -DKJ_HEADER_WARNINGS -DCAPNP_HEADER_WARNINGS -DCAPNP_INCLUDE_DIR=\"/usr/local/include\" -pthread -O2 -DNDEBUG -pthread -pthread -o .libs/capnp src/capnp/compiler/module-loader.o src/capnp/compiler/capnp.o ./.libs/libcapnpc.a ./.libs/libcapnp.a ./.libs/libkj.a -lpthread -pthread
|
||||
|
||||
g++ -std=gnu++11 -I./src -I./src -DKJ_HEADER_WARNINGS -DCAPNP_HEADER_WARNINGS -DCAPNP_INCLUDE_DIR=\"/usr/local/include\" -pthread -O2 -DNDEBUG -pthread -pthread -o .libs/capnpc-c++ src/capnp/compiler/capnpc-c++.o ./.libs/libcapnp.a ./.libs/libkj.a -lpthread -pthread
|
||||
|
||||
g++ -std=gnu++11 -I./src -I./src -DKJ_HEADER_WARNINGS -DCAPNP_HEADER_WARNINGS -DCAPNP_INCLUDE_DIR=\"/usr/local/include\" -pthread -O2 -DNDEBUG -pthread -pthread -o .libs/capnpc-capnp src/capnp/compiler/capnpc-capnp.o ./.libs/libcapnp.a ./.libs/libkj.a -lpthread -pthread
|
||||
|
||||
cp .libs/capnp /usr/local/bin/
|
||||
ln -s /usr/local/bin/capnp /usr/local/bin/capnpc
|
||||
cp .libs/capnpc-c++ /usr/local/bin/
|
||||
cp .libs/capnpc-capnp /usr/local/bin/
|
||||
cp .libs/*.a /usr/local/lib
|
||||
|
||||
cd /tmp
|
||||
echo "Installing c-capnp"
|
||||
git clone https://github.com/commaai/c-capnproto.git
|
||||
cd c-capnproto
|
||||
git submodule update --init --recursive
|
||||
autoreconf -f -i -s
|
||||
CXXFLAGS="-fPIC" ./configure
|
||||
make -j4
|
||||
|
||||
# manually build binaries statically
|
||||
gcc -fPIC -o .libs/capnpc-c compiler/capnpc-c.o compiler/schema.capnp.o compiler/str.o ./.libs/libcapnp_c.a
|
||||
|
||||
cp .libs/capnpc-c /usr/local/bin/
|
||||
cp .libs/*.a /usr/local/lib
|
||||
1862
cereal/log.capnp
Normal file
1862
cereal/log.capnp
Normal file
File diff suppressed because it is too large
Load Diff
53
cereal/maptile.capnp
Normal file
53
cereal/maptile.capnp
Normal file
@@ -0,0 +1,53 @@
|
||||
using Cxx = import "./include/c++.capnp";
|
||||
$Cxx.namespace("cereal");
|
||||
|
||||
using Java = import "./include/java.capnp";
|
||||
$Java.package("ai.comma.openpilot.cereal");
|
||||
$Java.outerClassname("Map");
|
||||
|
||||
@0xa086df597ef5d7a0;
|
||||
|
||||
# Geometry
|
||||
struct Point {
|
||||
x @0: Float64;
|
||||
y @1: Float64;
|
||||
z @2: Float64;
|
||||
}
|
||||
|
||||
struct PolyLine {
|
||||
points @0: List(Point);
|
||||
}
|
||||
|
||||
# Map features
|
||||
struct Lane {
|
||||
id @0 :Text;
|
||||
|
||||
leftBoundary @1 :LaneBoundary;
|
||||
rightBoundary @2 :LaneBoundary;
|
||||
|
||||
leftAdjacentId @3 :Text;
|
||||
rightAdjacentId @4 :Text;
|
||||
|
||||
inboundIds @5 :List(Text);
|
||||
outboundIds @6 :List(Text);
|
||||
|
||||
struct LaneBoundary {
|
||||
polyLine @0 :PolyLine;
|
||||
startHeading @1 :Float32; # WRT north
|
||||
}
|
||||
}
|
||||
|
||||
# Map tiles
|
||||
struct TileSummary {
|
||||
version @0 :Text;
|
||||
updatedAt @1 :UInt64; # Millis since epoch
|
||||
|
||||
level @2 :UInt8;
|
||||
x @3 :UInt16;
|
||||
y @4 :UInt16;
|
||||
}
|
||||
|
||||
struct MapTile {
|
||||
summary @0 :TileSummary;
|
||||
lanes @1 :List(Lane);
|
||||
}
|
||||
12
check_code_quality.sh
Normal file
12
check_code_quality.sh
Normal file
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Only pyflakes checks (--select=F)
|
||||
flake8 --select=F $(find . -iname "*.py" | grep -vi "^\./pyextra.*" | grep -vi "^\./panda")
|
||||
RESULT=$?
|
||||
if [ $RESULT -eq 0 ]; then
|
||||
pylint $(find . -iname "*.py" | grep -vi "^\./pyextra.*" | grep -vi "^\./panda")
|
||||
RESULT=$? & 3
|
||||
fi
|
||||
|
||||
[ $RESULT -ne 0 ] && exit 1
|
||||
exit 0
|
||||
@@ -1,13 +0,0 @@
|
||||
Metadata-Version: 1.1
|
||||
Name: click
|
||||
Version: 6.7
|
||||
Summary: A simple wrapper around optparse for powerful command line utilities.
|
||||
Home-page: http://github.com/mitsuhiko/click
|
||||
Author: Armin Ronacher
|
||||
Author-email: armin.ronacher@active-4.com
|
||||
License: UNKNOWN
|
||||
Description: UNKNOWN
|
||||
Platform: UNKNOWN
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
@@ -1,114 +0,0 @@
|
||||
CHANGES
|
||||
LICENSE
|
||||
MANIFEST.in
|
||||
Makefile
|
||||
README
|
||||
setup.cfg
|
||||
setup.py
|
||||
artwork/logo.svg
|
||||
click/__init__.py
|
||||
click/_bashcomplete.py
|
||||
click/_compat.py
|
||||
click/_termui_impl.py
|
||||
click/_textwrap.py
|
||||
click/_unicodefun.py
|
||||
click/_winconsole.py
|
||||
click/core.py
|
||||
click/decorators.py
|
||||
click/exceptions.py
|
||||
click/formatting.py
|
||||
click/globals.py
|
||||
click/parser.py
|
||||
click/termui.py
|
||||
click/testing.py
|
||||
click/types.py
|
||||
click/utils.py
|
||||
click.egg-info/PKG-INFO
|
||||
click.egg-info/SOURCES.txt
|
||||
click.egg-info/dependency_links.txt
|
||||
click.egg-info/top_level.txt
|
||||
docs/Makefile
|
||||
docs/advanced.rst
|
||||
docs/api.rst
|
||||
docs/arguments.rst
|
||||
docs/bashcomplete.rst
|
||||
docs/changelog.rst
|
||||
docs/clickdoctools.py
|
||||
docs/commands.rst
|
||||
docs/complex.rst
|
||||
docs/conf.py
|
||||
docs/contrib.rst
|
||||
docs/documentation.rst
|
||||
docs/exceptions.rst
|
||||
docs/index.rst
|
||||
docs/license.rst
|
||||
docs/make.bat
|
||||
docs/options.rst
|
||||
docs/parameters.rst
|
||||
docs/prompts.rst
|
||||
docs/python3.rst
|
||||
docs/quickstart.rst
|
||||
docs/setuptools.rst
|
||||
docs/testing.rst
|
||||
docs/upgrading.rst
|
||||
docs/utils.rst
|
||||
docs/why.rst
|
||||
docs/wincmd.rst
|
||||
docs/_static/click-small.png
|
||||
docs/_static/click-small@2x.png
|
||||
docs/_static/click.png
|
||||
docs/_static/click@2x.png
|
||||
docs/_templates/sidebarintro.html
|
||||
docs/_templates/sidebarlogo.html
|
||||
examples/README
|
||||
examples/aliases/README
|
||||
examples/aliases/aliases.ini
|
||||
examples/aliases/aliases.py
|
||||
examples/aliases/setup.py
|
||||
examples/colors/README
|
||||
examples/colors/colors.py
|
||||
examples/colors/setup.py
|
||||
examples/complex/README
|
||||
examples/complex/setup.py
|
||||
examples/complex/complex/__init__.py
|
||||
examples/complex/complex/cli.py
|
||||
examples/complex/complex/commands/__init__.py
|
||||
examples/complex/complex/commands/cmd_init.py
|
||||
examples/complex/complex/commands/cmd_status.py
|
||||
examples/imagepipe/.gitignore
|
||||
examples/imagepipe/README
|
||||
examples/imagepipe/example01.jpg
|
||||
examples/imagepipe/example02.jpg
|
||||
examples/imagepipe/imagepipe.py
|
||||
examples/imagepipe/setup.py
|
||||
examples/inout/README
|
||||
examples/inout/inout.py
|
||||
examples/inout/setup.py
|
||||
examples/naval/README
|
||||
examples/naval/naval.py
|
||||
examples/naval/setup.py
|
||||
examples/repo/README
|
||||
examples/repo/repo.py
|
||||
examples/repo/setup.py
|
||||
examples/termui/README
|
||||
examples/termui/setup.py
|
||||
examples/termui/termui.py
|
||||
examples/validation/README
|
||||
examples/validation/setup.py
|
||||
examples/validation/validation.py
|
||||
tests/conftest.py
|
||||
tests/test_arguments.py
|
||||
tests/test_bashcomplete.py
|
||||
tests/test_basic.py
|
||||
tests/test_chain.py
|
||||
tests/test_commands.py
|
||||
tests/test_compat.py
|
||||
tests/test_context.py
|
||||
tests/test_defaults.py
|
||||
tests/test_formatting.py
|
||||
tests/test_imports.py
|
||||
tests/test_normalization.py
|
||||
tests/test_options.py
|
||||
tests/test_termui.py
|
||||
tests/test_testing.py
|
||||
tests/test_utils.py
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
../click/exceptions.py
|
||||
../click/testing.py
|
||||
../click/decorators.py
|
||||
../click/parser.py
|
||||
../click/formatting.py
|
||||
../click/globals.py
|
||||
../click/_termui_impl.py
|
||||
../click/__init__.py
|
||||
../click/_compat.py
|
||||
../click/_winconsole.py
|
||||
../click/_unicodefun.py
|
||||
../click/_textwrap.py
|
||||
../click/_bashcomplete.py
|
||||
../click/core.py
|
||||
../click/types.py
|
||||
../click/termui.py
|
||||
../click/utils.py
|
||||
../click/exceptions.pyc
|
||||
../click/testing.pyc
|
||||
../click/decorators.pyc
|
||||
../click/parser.pyc
|
||||
../click/formatting.pyc
|
||||
../click/globals.pyc
|
||||
../click/_termui_impl.pyc
|
||||
../click/__init__.pyc
|
||||
../click/_compat.pyc
|
||||
../click/_winconsole.pyc
|
||||
../click/_unicodefun.pyc
|
||||
../click/_textwrap.pyc
|
||||
../click/_bashcomplete.pyc
|
||||
../click/core.pyc
|
||||
../click/types.pyc
|
||||
../click/termui.pyc
|
||||
../click/utils.pyc
|
||||
SOURCES.txt
|
||||
top_level.txt
|
||||
PKG-INFO
|
||||
dependency_links.txt
|
||||
@@ -1 +0,0 @@
|
||||
click
|
||||
@@ -1,98 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
click
|
||||
~~~~~
|
||||
|
||||
Click is a simple Python module that wraps the stdlib's optparse to make
|
||||
writing command line scripts fun. Unlike other modules, it's based around
|
||||
a simple API that does not come with too much magic and is composable.
|
||||
|
||||
In case optparse ever gets removed from the stdlib, it will be shipped by
|
||||
this module.
|
||||
|
||||
:copyright: (c) 2014 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
|
||||
# Core classes
|
||||
from .core import Context, BaseCommand, Command, MultiCommand, Group, \
|
||||
CommandCollection, Parameter, Option, Argument
|
||||
|
||||
# Globals
|
||||
from .globals import get_current_context
|
||||
|
||||
# Decorators
|
||||
from .decorators import pass_context, pass_obj, make_pass_decorator, \
|
||||
command, group, argument, option, confirmation_option, \
|
||||
password_option, version_option, help_option
|
||||
|
||||
# Types
|
||||
from .types import ParamType, File, Path, Choice, IntRange, Tuple, \
|
||||
STRING, INT, FLOAT, BOOL, UUID, UNPROCESSED
|
||||
|
||||
# Utilities
|
||||
from .utils import echo, get_binary_stream, get_text_stream, open_file, \
|
||||
format_filename, get_app_dir, get_os_args
|
||||
|
||||
# Terminal functions
|
||||
from .termui import prompt, confirm, get_terminal_size, echo_via_pager, \
|
||||
progressbar, clear, style, unstyle, secho, edit, launch, getchar, \
|
||||
pause
|
||||
|
||||
# Exceptions
|
||||
from .exceptions import ClickException, UsageError, BadParameter, \
|
||||
FileError, Abort, NoSuchOption, BadOptionUsage, BadArgumentUsage, \
|
||||
MissingParameter
|
||||
|
||||
# Formatting
|
||||
from .formatting import HelpFormatter, wrap_text
|
||||
|
||||
# Parsing
|
||||
from .parser import OptionParser
|
||||
|
||||
|
||||
__all__ = [
|
||||
# Core classes
|
||||
'Context', 'BaseCommand', 'Command', 'MultiCommand', 'Group',
|
||||
'CommandCollection', 'Parameter', 'Option', 'Argument',
|
||||
|
||||
# Globals
|
||||
'get_current_context',
|
||||
|
||||
# Decorators
|
||||
'pass_context', 'pass_obj', 'make_pass_decorator', 'command', 'group',
|
||||
'argument', 'option', 'confirmation_option', 'password_option',
|
||||
'version_option', 'help_option',
|
||||
|
||||
# Types
|
||||
'ParamType', 'File', 'Path', 'Choice', 'IntRange', 'Tuple', 'STRING',
|
||||
'INT', 'FLOAT', 'BOOL', 'UUID', 'UNPROCESSED',
|
||||
|
||||
# Utilities
|
||||
'echo', 'get_binary_stream', 'get_text_stream', 'open_file',
|
||||
'format_filename', 'get_app_dir', 'get_os_args',
|
||||
|
||||
# Terminal functions
|
||||
'prompt', 'confirm', 'get_terminal_size', 'echo_via_pager',
|
||||
'progressbar', 'clear', 'style', 'unstyle', 'secho', 'edit', 'launch',
|
||||
'getchar', 'pause',
|
||||
|
||||
# Exceptions
|
||||
'ClickException', 'UsageError', 'BadParameter', 'FileError',
|
||||
'Abort', 'NoSuchOption', 'BadOptionUsage', 'BadArgumentUsage',
|
||||
'MissingParameter',
|
||||
|
||||
# Formatting
|
||||
'HelpFormatter', 'wrap_text',
|
||||
|
||||
# Parsing
|
||||
'OptionParser',
|
||||
]
|
||||
|
||||
|
||||
# Controls if click should emit the warning about the use of unicode
|
||||
# literals.
|
||||
disable_unicode_literals_warning = False
|
||||
|
||||
|
||||
__version__ = '6.7'
|
||||
@@ -1,83 +0,0 @@
|
||||
import os
|
||||
import re
|
||||
from .utils import echo
|
||||
from .parser import split_arg_string
|
||||
from .core import MultiCommand, Option
|
||||
|
||||
|
||||
COMPLETION_SCRIPT = '''
|
||||
%(complete_func)s() {
|
||||
COMPREPLY=( $( env COMP_WORDS="${COMP_WORDS[*]}" \\
|
||||
COMP_CWORD=$COMP_CWORD \\
|
||||
%(autocomplete_var)s=complete $1 ) )
|
||||
return 0
|
||||
}
|
||||
|
||||
complete -F %(complete_func)s -o default %(script_names)s
|
||||
'''
|
||||
|
||||
_invalid_ident_char_re = re.compile(r'[^a-zA-Z0-9_]')
|
||||
|
||||
|
||||
def get_completion_script(prog_name, complete_var):
|
||||
cf_name = _invalid_ident_char_re.sub('', prog_name.replace('-', '_'))
|
||||
return (COMPLETION_SCRIPT % {
|
||||
'complete_func': '_%s_completion' % cf_name,
|
||||
'script_names': prog_name,
|
||||
'autocomplete_var': complete_var,
|
||||
}).strip() + ';'
|
||||
|
||||
|
||||
def resolve_ctx(cli, prog_name, args):
|
||||
ctx = cli.make_context(prog_name, args, resilient_parsing=True)
|
||||
while ctx.protected_args + ctx.args and isinstance(ctx.command, MultiCommand):
|
||||
a = ctx.protected_args + ctx.args
|
||||
cmd = ctx.command.get_command(ctx, a[0])
|
||||
if cmd is None:
|
||||
return None
|
||||
ctx = cmd.make_context(a[0], a[1:], parent=ctx, resilient_parsing=True)
|
||||
return ctx
|
||||
|
||||
|
||||
def get_choices(cli, prog_name, args, incomplete):
|
||||
ctx = resolve_ctx(cli, prog_name, args)
|
||||
if ctx is None:
|
||||
return
|
||||
|
||||
choices = []
|
||||
if incomplete and not incomplete[:1].isalnum():
|
||||
for param in ctx.command.params:
|
||||
if not isinstance(param, Option):
|
||||
continue
|
||||
choices.extend(param.opts)
|
||||
choices.extend(param.secondary_opts)
|
||||
elif isinstance(ctx.command, MultiCommand):
|
||||
choices.extend(ctx.command.list_commands(ctx))
|
||||
|
||||
for item in choices:
|
||||
if item.startswith(incomplete):
|
||||
yield item
|
||||
|
||||
|
||||
def do_complete(cli, prog_name):
|
||||
cwords = split_arg_string(os.environ['COMP_WORDS'])
|
||||
cword = int(os.environ['COMP_CWORD'])
|
||||
args = cwords[1:cword]
|
||||
try:
|
||||
incomplete = cwords[cword]
|
||||
except IndexError:
|
||||
incomplete = ''
|
||||
|
||||
for item in get_choices(cli, prog_name, args, incomplete):
|
||||
echo(item)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def bashcomplete(cli, prog_name, complete_var, complete_instr):
|
||||
if complete_instr == 'source':
|
||||
echo(get_completion_script(prog_name, complete_var))
|
||||
return True
|
||||
elif complete_instr == 'complete':
|
||||
return do_complete(cli, prog_name)
|
||||
return False
|
||||
648
click/_compat.py
648
click/_compat.py
@@ -1,648 +0,0 @@
|
||||
import re
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
import codecs
|
||||
from weakref import WeakKeyDictionary
|
||||
|
||||
|
||||
PY2 = sys.version_info[0] == 2
|
||||
WIN = sys.platform.startswith('win')
|
||||
DEFAULT_COLUMNS = 80
|
||||
|
||||
|
||||
_ansi_re = re.compile('\033\[((?:\d|;)*)([a-zA-Z])')
|
||||
|
||||
|
||||
def get_filesystem_encoding():
|
||||
return sys.getfilesystemencoding() or sys.getdefaultencoding()
|
||||
|
||||
|
||||
def _make_text_stream(stream, encoding, errors):
|
||||
if encoding is None:
|
||||
encoding = get_best_encoding(stream)
|
||||
if errors is None:
|
||||
errors = 'replace'
|
||||
return _NonClosingTextIOWrapper(stream, encoding, errors,
|
||||
line_buffering=True)
|
||||
|
||||
|
||||
def is_ascii_encoding(encoding):
|
||||
"""Checks if a given encoding is ascii."""
|
||||
try:
|
||||
return codecs.lookup(encoding).name == 'ascii'
|
||||
except LookupError:
|
||||
return False
|
||||
|
||||
|
||||
def get_best_encoding(stream):
|
||||
"""Returns the default stream encoding if not found."""
|
||||
rv = getattr(stream, 'encoding', None) or sys.getdefaultencoding()
|
||||
if is_ascii_encoding(rv):
|
||||
return 'utf-8'
|
||||
return rv
|
||||
|
||||
|
||||
class _NonClosingTextIOWrapper(io.TextIOWrapper):
|
||||
|
||||
def __init__(self, stream, encoding, errors, **extra):
|
||||
self._stream = stream = _FixupStream(stream)
|
||||
io.TextIOWrapper.__init__(self, stream, encoding, errors, **extra)
|
||||
|
||||
# The io module is a place where the Python 3 text behavior
|
||||
# was forced upon Python 2, so we need to unbreak
|
||||
# it to look like Python 2.
|
||||
if PY2:
|
||||
def write(self, x):
|
||||
if isinstance(x, str) or is_bytes(x):
|
||||
try:
|
||||
self.flush()
|
||||
except Exception:
|
||||
pass
|
||||
return self.buffer.write(str(x))
|
||||
return io.TextIOWrapper.write(self, x)
|
||||
|
||||
def writelines(self, lines):
|
||||
for line in lines:
|
||||
self.write(line)
|
||||
|
||||
def __del__(self):
|
||||
try:
|
||||
self.detach()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def isatty(self):
|
||||
# https://bitbucket.org/pypy/pypy/issue/1803
|
||||
return self._stream.isatty()
|
||||
|
||||
|
||||
class _FixupStream(object):
|
||||
"""The new io interface needs more from streams than streams
|
||||
traditionally implement. As such, this fix-up code is necessary in
|
||||
some circumstances.
|
||||
"""
|
||||
|
||||
def __init__(self, stream):
|
||||
self._stream = stream
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self._stream, name)
|
||||
|
||||
def read1(self, size):
|
||||
f = getattr(self._stream, 'read1', None)
|
||||
if f is not None:
|
||||
return f(size)
|
||||
# We only dispatch to readline instead of read in Python 2 as we
|
||||
# do not want cause problems with the different implementation
|
||||
# of line buffering.
|
||||
if PY2:
|
||||
return self._stream.readline(size)
|
||||
return self._stream.read(size)
|
||||
|
||||
def readable(self):
|
||||
x = getattr(self._stream, 'readable', None)
|
||||
if x is not None:
|
||||
return x()
|
||||
try:
|
||||
self._stream.read(0)
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
|
||||
def writable(self):
|
||||
x = getattr(self._stream, 'writable', None)
|
||||
if x is not None:
|
||||
return x()
|
||||
try:
|
||||
self._stream.write('')
|
||||
except Exception:
|
||||
try:
|
||||
self._stream.write(b'')
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
|
||||
def seekable(self):
|
||||
x = getattr(self._stream, 'seekable', None)
|
||||
if x is not None:
|
||||
return x()
|
||||
try:
|
||||
self._stream.seek(self._stream.tell())
|
||||
except Exception:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
if PY2:
|
||||
text_type = unicode
|
||||
bytes = str
|
||||
raw_input = raw_input
|
||||
string_types = (str, unicode)
|
||||
iteritems = lambda x: x.iteritems()
|
||||
range_type = xrange
|
||||
|
||||
def is_bytes(x):
|
||||
return isinstance(x, (buffer, bytearray))
|
||||
|
||||
_identifier_re = re.compile(r'^[a-zA-Z_][a-zA-Z0-9_]*$')
|
||||
|
||||
# For Windows, we need to force stdout/stdin/stderr to binary if it's
|
||||
# fetched for that. This obviously is not the most correct way to do
|
||||
# it as it changes global state. Unfortunately, there does not seem to
|
||||
# be a clear better way to do it as just reopening the file in binary
|
||||
# mode does not change anything.
|
||||
#
|
||||
# An option would be to do what Python 3 does and to open the file as
|
||||
# binary only, patch it back to the system, and then use a wrapper
|
||||
# stream that converts newlines. It's not quite clear what's the
|
||||
# correct option here.
|
||||
#
|
||||
# This code also lives in _winconsole for the fallback to the console
|
||||
# emulation stream.
|
||||
#
|
||||
# There are also Windows environments where the `msvcrt` module is not
|
||||
# available (which is why we use try-catch instead of the WIN variable
|
||||
# here), such as the Google App Engine development server on Windows. In
|
||||
# those cases there is just nothing we can do.
|
||||
try:
|
||||
import msvcrt
|
||||
except ImportError:
|
||||
set_binary_mode = lambda x: x
|
||||
else:
|
||||
def set_binary_mode(f):
|
||||
try:
|
||||
fileno = f.fileno()
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
msvcrt.setmode(fileno, os.O_BINARY)
|
||||
return f
|
||||
|
||||
def isidentifier(x):
|
||||
return _identifier_re.search(x) is not None
|
||||
|
||||
def get_binary_stdin():
|
||||
return set_binary_mode(sys.stdin)
|
||||
|
||||
def get_binary_stdout():
|
||||
return set_binary_mode(sys.stdout)
|
||||
|
||||
def get_binary_stderr():
|
||||
return set_binary_mode(sys.stderr)
|
||||
|
||||
def get_text_stdin(encoding=None, errors=None):
|
||||
rv = _get_windows_console_stream(sys.stdin, encoding, errors)
|
||||
if rv is not None:
|
||||
return rv
|
||||
return _make_text_stream(sys.stdin, encoding, errors)
|
||||
|
||||
def get_text_stdout(encoding=None, errors=None):
|
||||
rv = _get_windows_console_stream(sys.stdout, encoding, errors)
|
||||
if rv is not None:
|
||||
return rv
|
||||
return _make_text_stream(sys.stdout, encoding, errors)
|
||||
|
||||
def get_text_stderr(encoding=None, errors=None):
|
||||
rv = _get_windows_console_stream(sys.stderr, encoding, errors)
|
||||
if rv is not None:
|
||||
return rv
|
||||
return _make_text_stream(sys.stderr, encoding, errors)
|
||||
|
||||
def filename_to_ui(value):
|
||||
if isinstance(value, bytes):
|
||||
value = value.decode(get_filesystem_encoding(), 'replace')
|
||||
return value
|
||||
else:
|
||||
import io
|
||||
text_type = str
|
||||
raw_input = input
|
||||
string_types = (str,)
|
||||
range_type = range
|
||||
isidentifier = lambda x: x.isidentifier()
|
||||
iteritems = lambda x: iter(x.items())
|
||||
|
||||
def is_bytes(x):
|
||||
return isinstance(x, (bytes, memoryview, bytearray))
|
||||
|
||||
def _is_binary_reader(stream, default=False):
|
||||
try:
|
||||
return isinstance(stream.read(0), bytes)
|
||||
except Exception:
|
||||
return default
|
||||
# This happens in some cases where the stream was already
|
||||
# closed. In this case, we assume the default.
|
||||
|
||||
def _is_binary_writer(stream, default=False):
|
||||
try:
|
||||
stream.write(b'')
|
||||
except Exception:
|
||||
try:
|
||||
stream.write('')
|
||||
return False
|
||||
except Exception:
|
||||
pass
|
||||
return default
|
||||
return True
|
||||
|
||||
def _find_binary_reader(stream):
|
||||
# We need to figure out if the given stream is already binary.
|
||||
# This can happen because the official docs recommend detaching
|
||||
# the streams to get binary streams. Some code might do this, so
|
||||
# we need to deal with this case explicitly.
|
||||
if _is_binary_reader(stream, False):
|
||||
return stream
|
||||
|
||||
buf = getattr(stream, 'buffer', None)
|
||||
|
||||
# Same situation here; this time we assume that the buffer is
|
||||
# actually binary in case it's closed.
|
||||
if buf is not None and _is_binary_reader(buf, True):
|
||||
return buf
|
||||
|
||||
def _find_binary_writer(stream):
|
||||
# We need to figure out if the given stream is already binary.
|
||||
# This can happen because the official docs recommend detatching
|
||||
# the streams to get binary streams. Some code might do this, so
|
||||
# we need to deal with this case explicitly.
|
||||
if _is_binary_writer(stream, False):
|
||||
return stream
|
||||
|
||||
buf = getattr(stream, 'buffer', None)
|
||||
|
||||
# Same situation here; this time we assume that the buffer is
|
||||
# actually binary in case it's closed.
|
||||
if buf is not None and _is_binary_writer(buf, True):
|
||||
return buf
|
||||
|
||||
def _stream_is_misconfigured(stream):
|
||||
"""A stream is misconfigured if its encoding is ASCII."""
|
||||
# If the stream does not have an encoding set, we assume it's set
|
||||
# to ASCII. This appears to happen in certain unittest
|
||||
# environments. It's not quite clear what the correct behavior is
|
||||
# but this at least will force Click to recover somehow.
|
||||
return is_ascii_encoding(getattr(stream, 'encoding', None) or 'ascii')
|
||||
|
||||
def _is_compatible_text_stream(stream, encoding, errors):
|
||||
stream_encoding = getattr(stream, 'encoding', None)
|
||||
stream_errors = getattr(stream, 'errors', None)
|
||||
|
||||
# Perfect match.
|
||||
if stream_encoding == encoding and stream_errors == errors:
|
||||
return True
|
||||
|
||||
# Otherwise, it's only a compatible stream if we did not ask for
|
||||
# an encoding.
|
||||
if encoding is None:
|
||||
return stream_encoding is not None
|
||||
|
||||
return False
|
||||
|
||||
def _force_correct_text_reader(text_reader, encoding, errors):
|
||||
if _is_binary_reader(text_reader, False):
|
||||
binary_reader = text_reader
|
||||
else:
|
||||
# If there is no target encoding set, we need to verify that the
|
||||
# reader is not actually misconfigured.
|
||||
if encoding is None and not _stream_is_misconfigured(text_reader):
|
||||
return text_reader
|
||||
|
||||
if _is_compatible_text_stream(text_reader, encoding, errors):
|
||||
return text_reader
|
||||
|
||||
# If the reader has no encoding, we try to find the underlying
|
||||
# binary reader for it. If that fails because the environment is
|
||||
# misconfigured, we silently go with the same reader because this
|
||||
# is too common to happen. In that case, mojibake is better than
|
||||
# exceptions.
|
||||
binary_reader = _find_binary_reader(text_reader)
|
||||
if binary_reader is None:
|
||||
return text_reader
|
||||
|
||||
# At this point, we default the errors to replace instead of strict
|
||||
# because nobody handles those errors anyways and at this point
|
||||
# we're so fundamentally fucked that nothing can repair it.
|
||||
if errors is None:
|
||||
errors = 'replace'
|
||||
return _make_text_stream(binary_reader, encoding, errors)
|
||||
|
||||
def _force_correct_text_writer(text_writer, encoding, errors):
|
||||
if _is_binary_writer(text_writer, False):
|
||||
binary_writer = text_writer
|
||||
else:
|
||||
# If there is no target encoding set, we need to verify that the
|
||||
# writer is not actually misconfigured.
|
||||
if encoding is None and not _stream_is_misconfigured(text_writer):
|
||||
return text_writer
|
||||
|
||||
if _is_compatible_text_stream(text_writer, encoding, errors):
|
||||
return text_writer
|
||||
|
||||
# If the writer has no encoding, we try to find the underlying
|
||||
# binary writer for it. If that fails because the environment is
|
||||
# misconfigured, we silently go with the same writer because this
|
||||
# is too common to happen. In that case, mojibake is better than
|
||||
# exceptions.
|
||||
binary_writer = _find_binary_writer(text_writer)
|
||||
if binary_writer is None:
|
||||
return text_writer
|
||||
|
||||
# At this point, we default the errors to replace instead of strict
|
||||
# because nobody handles those errors anyways and at this point
|
||||
# we're so fundamentally fucked that nothing can repair it.
|
||||
if errors is None:
|
||||
errors = 'replace'
|
||||
return _make_text_stream(binary_writer, encoding, errors)
|
||||
|
||||
def get_binary_stdin():
|
||||
reader = _find_binary_reader(sys.stdin)
|
||||
if reader is None:
|
||||
raise RuntimeError('Was not able to determine binary '
|
||||
'stream for sys.stdin.')
|
||||
return reader
|
||||
|
||||
def get_binary_stdout():
|
||||
writer = _find_binary_writer(sys.stdout)
|
||||
if writer is None:
|
||||
raise RuntimeError('Was not able to determine binary '
|
||||
'stream for sys.stdout.')
|
||||
return writer
|
||||
|
||||
def get_binary_stderr():
|
||||
writer = _find_binary_writer(sys.stderr)
|
||||
if writer is None:
|
||||
raise RuntimeError('Was not able to determine binary '
|
||||
'stream for sys.stderr.')
|
||||
return writer
|
||||
|
||||
def get_text_stdin(encoding=None, errors=None):
|
||||
rv = _get_windows_console_stream(sys.stdin, encoding, errors)
|
||||
if rv is not None:
|
||||
return rv
|
||||
return _force_correct_text_reader(sys.stdin, encoding, errors)
|
||||
|
||||
def get_text_stdout(encoding=None, errors=None):
|
||||
rv = _get_windows_console_stream(sys.stdout, encoding, errors)
|
||||
if rv is not None:
|
||||
return rv
|
||||
return _force_correct_text_writer(sys.stdout, encoding, errors)
|
||||
|
||||
def get_text_stderr(encoding=None, errors=None):
|
||||
rv = _get_windows_console_stream(sys.stderr, encoding, errors)
|
||||
if rv is not None:
|
||||
return rv
|
||||
return _force_correct_text_writer(sys.stderr, encoding, errors)
|
||||
|
||||
def filename_to_ui(value):
|
||||
if isinstance(value, bytes):
|
||||
value = value.decode(get_filesystem_encoding(), 'replace')
|
||||
else:
|
||||
value = value.encode('utf-8', 'surrogateescape') \
|
||||
.decode('utf-8', 'replace')
|
||||
return value
|
||||
|
||||
|
||||
def get_streerror(e, default=None):
|
||||
if hasattr(e, 'strerror'):
|
||||
msg = e.strerror
|
||||
else:
|
||||
if default is not None:
|
||||
msg = default
|
||||
else:
|
||||
msg = str(e)
|
||||
if isinstance(msg, bytes):
|
||||
msg = msg.decode('utf-8', 'replace')
|
||||
return msg
|
||||
|
||||
|
||||
def open_stream(filename, mode='r', encoding=None, errors='strict',
|
||||
atomic=False):
|
||||
# Standard streams first. These are simple because they don't need
|
||||
# special handling for the atomic flag. It's entirely ignored.
|
||||
if filename == '-':
|
||||
if 'w' in mode:
|
||||
if 'b' in mode:
|
||||
return get_binary_stdout(), False
|
||||
return get_text_stdout(encoding=encoding, errors=errors), False
|
||||
if 'b' in mode:
|
||||
return get_binary_stdin(), False
|
||||
return get_text_stdin(encoding=encoding, errors=errors), False
|
||||
|
||||
# Non-atomic writes directly go out through the regular open functions.
|
||||
if not atomic:
|
||||
if encoding is None:
|
||||
return open(filename, mode), True
|
||||
return io.open(filename, mode, encoding=encoding, errors=errors), True
|
||||
|
||||
# Some usability stuff for atomic writes
|
||||
if 'a' in mode:
|
||||
raise ValueError(
|
||||
'Appending to an existing file is not supported, because that '
|
||||
'would involve an expensive `copy`-operation to a temporary '
|
||||
'file. Open the file in normal `w`-mode and copy explicitly '
|
||||
'if that\'s what you\'re after.'
|
||||
)
|
||||
if 'x' in mode:
|
||||
raise ValueError('Use the `overwrite`-parameter instead.')
|
||||
if 'w' not in mode:
|
||||
raise ValueError('Atomic writes only make sense with `w`-mode.')
|
||||
|
||||
# Atomic writes are more complicated. They work by opening a file
|
||||
# as a proxy in the same folder and then using the fdopen
|
||||
# functionality to wrap it in a Python file. Then we wrap it in an
|
||||
# atomic file that moves the file over on close.
|
||||
import tempfile
|
||||
fd, tmp_filename = tempfile.mkstemp(dir=os.path.dirname(filename),
|
||||
prefix='.__atomic-write')
|
||||
|
||||
if encoding is not None:
|
||||
f = io.open(fd, mode, encoding=encoding, errors=errors)
|
||||
else:
|
||||
f = os.fdopen(fd, mode)
|
||||
|
||||
return _AtomicFile(f, tmp_filename, filename), True
|
||||
|
||||
|
||||
# Used in a destructor call, needs extra protection from interpreter cleanup.
|
||||
if hasattr(os, 'replace'):
|
||||
_replace = os.replace
|
||||
_can_replace = True
|
||||
else:
|
||||
_replace = os.rename
|
||||
_can_replace = not WIN
|
||||
|
||||
|
||||
class _AtomicFile(object):
|
||||
|
||||
def __init__(self, f, tmp_filename, real_filename):
|
||||
self._f = f
|
||||
self._tmp_filename = tmp_filename
|
||||
self._real_filename = real_filename
|
||||
self.closed = False
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._real_filename
|
||||
|
||||
def close(self, delete=False):
|
||||
if self.closed:
|
||||
return
|
||||
self._f.close()
|
||||
if not _can_replace:
|
||||
try:
|
||||
os.remove(self._real_filename)
|
||||
except OSError:
|
||||
pass
|
||||
_replace(self._tmp_filename, self._real_filename)
|
||||
self.closed = True
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self._f, name)
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, tb):
|
||||
self.close(delete=exc_type is not None)
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self._f)
|
||||
|
||||
|
||||
auto_wrap_for_ansi = None
|
||||
colorama = None
|
||||
get_winterm_size = None
|
||||
|
||||
|
||||
def strip_ansi(value):
|
||||
return _ansi_re.sub('', value)
|
||||
|
||||
|
||||
def should_strip_ansi(stream=None, color=None):
|
||||
if color is None:
|
||||
if stream is None:
|
||||
stream = sys.stdin
|
||||
return not isatty(stream)
|
||||
return not color
|
||||
|
||||
|
||||
# If we're on Windows, we provide transparent integration through
|
||||
# colorama. This will make ANSI colors through the echo function
|
||||
# work automatically.
|
||||
if WIN:
|
||||
# Windows has a smaller terminal
|
||||
DEFAULT_COLUMNS = 79
|
||||
|
||||
from ._winconsole import _get_windows_console_stream
|
||||
|
||||
def _get_argv_encoding():
|
||||
import locale
|
||||
return locale.getpreferredencoding()
|
||||
|
||||
if PY2:
|
||||
def raw_input(prompt=''):
|
||||
sys.stderr.flush()
|
||||
if prompt:
|
||||
stdout = _default_text_stdout()
|
||||
stdout.write(prompt)
|
||||
stdin = _default_text_stdin()
|
||||
return stdin.readline().rstrip('\r\n')
|
||||
|
||||
try:
|
||||
import colorama
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
_ansi_stream_wrappers = WeakKeyDictionary()
|
||||
|
||||
def auto_wrap_for_ansi(stream, color=None):
|
||||
"""This function wraps a stream so that calls through colorama
|
||||
are issued to the win32 console API to recolor on demand. It
|
||||
also ensures to reset the colors if a write call is interrupted
|
||||
to not destroy the console afterwards.
|
||||
"""
|
||||
try:
|
||||
cached = _ansi_stream_wrappers.get(stream)
|
||||
except Exception:
|
||||
cached = None
|
||||
if cached is not None:
|
||||
return cached
|
||||
strip = should_strip_ansi(stream, color)
|
||||
ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip)
|
||||
rv = ansi_wrapper.stream
|
||||
_write = rv.write
|
||||
|
||||
def _safe_write(s):
|
||||
try:
|
||||
return _write(s)
|
||||
except:
|
||||
ansi_wrapper.reset_all()
|
||||
raise
|
||||
|
||||
rv.write = _safe_write
|
||||
try:
|
||||
_ansi_stream_wrappers[stream] = rv
|
||||
except Exception:
|
||||
pass
|
||||
return rv
|
||||
|
||||
def get_winterm_size():
|
||||
win = colorama.win32.GetConsoleScreenBufferInfo(
|
||||
colorama.win32.STDOUT).srWindow
|
||||
return win.Right - win.Left, win.Bottom - win.Top
|
||||
else:
|
||||
def _get_argv_encoding():
|
||||
return getattr(sys.stdin, 'encoding', None) or get_filesystem_encoding()
|
||||
|
||||
_get_windows_console_stream = lambda *x: None
|
||||
|
||||
|
||||
def term_len(x):
|
||||
return len(strip_ansi(x))
|
||||
|
||||
|
||||
def isatty(stream):
|
||||
try:
|
||||
return stream.isatty()
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
def _make_cached_stream_func(src_func, wrapper_func):
|
||||
cache = WeakKeyDictionary()
|
||||
def func():
|
||||
stream = src_func()
|
||||
try:
|
||||
rv = cache.get(stream)
|
||||
except Exception:
|
||||
rv = None
|
||||
if rv is not None:
|
||||
return rv
|
||||
rv = wrapper_func()
|
||||
try:
|
||||
cache[stream] = rv
|
||||
except Exception:
|
||||
pass
|
||||
return rv
|
||||
return func
|
||||
|
||||
|
||||
_default_text_stdin = _make_cached_stream_func(
|
||||
lambda: sys.stdin, get_text_stdin)
|
||||
_default_text_stdout = _make_cached_stream_func(
|
||||
lambda: sys.stdout, get_text_stdout)
|
||||
_default_text_stderr = _make_cached_stream_func(
|
||||
lambda: sys.stderr, get_text_stderr)
|
||||
|
||||
|
||||
binary_streams = {
|
||||
'stdin': get_binary_stdin,
|
||||
'stdout': get_binary_stdout,
|
||||
'stderr': get_binary_stderr,
|
||||
}
|
||||
|
||||
text_streams = {
|
||||
'stdin': get_text_stdin,
|
||||
'stdout': get_text_stdout,
|
||||
'stderr': get_text_stderr,
|
||||
}
|
||||
@@ -1,547 +0,0 @@
|
||||
"""
|
||||
click._termui_impl
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This module contains implementations for the termui module. To keep the
|
||||
import time of Click down, some infrequently used functionality is placed
|
||||
in this module and only imported as needed.
|
||||
|
||||
:copyright: (c) 2014 by Armin Ronacher.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import math
|
||||
from ._compat import _default_text_stdout, range_type, PY2, isatty, \
|
||||
open_stream, strip_ansi, term_len, get_best_encoding, WIN
|
||||
from .utils import echo
|
||||
from .exceptions import ClickException
|
||||
|
||||
|
||||
if os.name == 'nt':
|
||||
BEFORE_BAR = '\r'
|
||||
AFTER_BAR = '\n'
|
||||
else:
|
||||
BEFORE_BAR = '\r\033[?25l'
|
||||
AFTER_BAR = '\033[?25h\n'
|
||||
|
||||
|
||||
def _length_hint(obj):
|
||||
"""Returns the length hint of an object."""
|
||||
try:
|
||||
return len(obj)
|
||||
except (AttributeError, TypeError):
|
||||
try:
|
||||
get_hint = type(obj).__length_hint__
|
||||
except AttributeError:
|
||||
return None
|
||||
try:
|
||||
hint = get_hint(obj)
|
||||
except TypeError:
|
||||
return None
|
||||
if hint is NotImplemented or \
|
||||
not isinstance(hint, (int, long)) or \
|
||||
hint < 0:
|
||||
return None
|
||||
return hint
|
||||
|
||||
|
||||
class ProgressBar(object):
|
||||
|
||||
def __init__(self, iterable, length=None, fill_char='#', empty_char=' ',
|
||||
bar_template='%(bar)s', info_sep=' ', show_eta=True,
|
||||
show_percent=None, show_pos=False, item_show_func=None,
|
||||
label=None, file=None, color=None, width=30):
|
||||
self.fill_char = fill_char
|
||||
self.empty_char = empty_char
|
||||
self.bar_template = bar_template
|
||||
self.info_sep = info_sep
|
||||
self.show_eta = show_eta
|
||||
self.show_percent = show_percent
|
||||
self.show_pos = show_pos
|
||||
self.item_show_func = item_show_func
|
||||
self.label = label or ''
|
||||
if file is None:
|
||||
file = _default_text_stdout()
|
||||
self.file = file
|
||||
self.color = color
|
||||
self.width = width
|
||||
self.autowidth = width == 0
|
||||
|
||||
if length is None:
|
||||
length = _length_hint(iterable)
|
||||
if iterable is None:
|
||||
if length is None:
|
||||
raise TypeError('iterable or length is required')
|
||||
iterable = range_type(length)
|
||||
self.iter = iter(iterable)
|
||||
self.length = length
|
||||
self.length_known = length is not None
|
||||
self.pos = 0
|
||||
self.avg = []
|
||||
self.start = self.last_eta = time.time()
|
||||
self.eta_known = False
|
||||
self.finished = False
|
||||
self.max_width = None
|
||||
self.entered = False
|
||||
self.current_item = None
|
||||
self.is_hidden = not isatty(self.file)
|
||||
self._last_line = None
|
||||
|
||||
def __enter__(self):
|
||||
self.entered = True
|
||||
self.render_progress()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, tb):
|
||||
self.render_finish()
|
||||
|
||||
def __iter__(self):
|
||||
if not self.entered:
|
||||
raise RuntimeError('You need to use progress bars in a with block.')
|
||||
self.render_progress()
|
||||
return self
|
||||
|
||||
def render_finish(self):
|
||||
if self.is_hidden:
|
||||
return
|
||||
self.file.write(AFTER_BAR)
|
||||
self.file.flush()
|
||||
|
||||
@property
|
||||
def pct(self):
|
||||
if self.finished:
|
||||
return 1.0
|
||||
return min(self.pos / (float(self.length) or 1), 1.0)
|
||||
|
||||
@property
|
||||
def time_per_iteration(self):
|
||||
if not self.avg:
|
||||
return 0.0
|
||||
return sum(self.avg) / float(len(self.avg))
|
||||
|
||||
@property
|
||||
def eta(self):
|
||||
if self.length_known and not self.finished:
|
||||
return self.time_per_iteration * (self.length - self.pos)
|
||||
return 0.0
|
||||
|
||||
def format_eta(self):
|
||||
if self.eta_known:
|
||||
t = self.eta + 1
|
||||
seconds = t % 60
|
||||
t /= 60
|
||||
minutes = t % 60
|
||||
t /= 60
|
||||
hours = t % 24
|
||||
t /= 24
|
||||
if t > 0:
|
||||
days = t
|
||||
return '%dd %02d:%02d:%02d' % (days, hours, minutes, seconds)
|
||||
else:
|
||||
return '%02d:%02d:%02d' % (hours, minutes, seconds)
|
||||
return ''
|
||||
|
||||
def format_pos(self):
|
||||
pos = str(self.pos)
|
||||
if self.length_known:
|
||||
pos += '/%s' % self.length
|
||||
return pos
|
||||
|
||||
def format_pct(self):
|
||||
return ('% 4d%%' % int(self.pct * 100))[1:]
|
||||
|
||||
def format_progress_line(self):
|
||||
show_percent = self.show_percent
|
||||
|
||||
info_bits = []
|
||||
if self.length_known:
|
||||
bar_length = int(self.pct * self.width)
|
||||
bar = self.fill_char * bar_length
|
||||
bar += self.empty_char * (self.width - bar_length)
|
||||
if show_percent is None:
|
||||
show_percent = not self.show_pos
|
||||
else:
|
||||
if self.finished:
|
||||
bar = self.fill_char * self.width
|
||||
else:
|
||||
bar = list(self.empty_char * (self.width or 1))
|
||||
if self.time_per_iteration != 0:
|
||||
bar[int((math.cos(self.pos * self.time_per_iteration)
|
||||
/ 2.0 + 0.5) * self.width)] = self.fill_char
|
||||
bar = ''.join(bar)
|
||||
|
||||
if self.show_pos:
|
||||
info_bits.append(self.format_pos())
|
||||
if show_percent:
|
||||
info_bits.append(self.format_pct())
|
||||
if self.show_eta and self.eta_known and not self.finished:
|
||||
info_bits.append(self.format_eta())
|
||||
if self.item_show_func is not None:
|
||||
item_info = self.item_show_func(self.current_item)
|
||||
if item_info is not None:
|
||||
info_bits.append(item_info)
|
||||
|
||||
return (self.bar_template % {
|
||||
'label': self.label,
|
||||
'bar': bar,
|
||||
'info': self.info_sep.join(info_bits)
|
||||
}).rstrip()
|
||||
|
||||
def render_progress(self):
|
||||
from .termui import get_terminal_size
|
||||
nl = False
|
||||
|
||||
if self.is_hidden:
|
||||
buf = [self.label]
|
||||
nl = True
|
||||
else:
|
||||
buf = []
|
||||
# Update width in case the terminal has been resized
|
||||
if self.autowidth:
|
||||
old_width = self.width
|
||||
self.width = 0
|
||||
clutter_length = term_len(self.format_progress_line())
|
||||
new_width = max(0, get_terminal_size()[0] - clutter_length)
|
||||
if new_width < old_width:
|
||||
buf.append(BEFORE_BAR)
|
||||
buf.append(' ' * self.max_width)
|
||||
self.max_width = new_width
|
||||
self.width = new_width
|
||||
|
||||
clear_width = self.width
|
||||
if self.max_width is not None:
|
||||
clear_width = self.max_width
|
||||
|
||||
buf.append(BEFORE_BAR)
|
||||
line = self.format_progress_line()
|
||||
line_len = term_len(line)
|
||||
if self.max_width is None or self.max_width < line_len:
|
||||
self.max_width = line_len
|
||||
buf.append(line)
|
||||
|
||||
buf.append(' ' * (clear_width - line_len))
|
||||
line = ''.join(buf)
|
||||
|
||||
# Render the line only if it changed.
|
||||
if line != self._last_line:
|
||||
self._last_line = line
|
||||
echo(line, file=self.file, color=self.color, nl=nl)
|
||||
self.file.flush()
|
||||
|
||||
def make_step(self, n_steps):
|
||||
self.pos += n_steps
|
||||
if self.length_known and self.pos >= self.length:
|
||||
self.finished = True
|
||||
|
||||
if (time.time() - self.last_eta) < 1.0:
|
||||
return
|
||||
|
||||
self.last_eta = time.time()
|
||||
self.avg = self.avg[-6:] + [-(self.start - time.time()) / (self.pos)]
|
||||
|
||||
self.eta_known = self.length_known
|
||||
|
||||
def update(self, n_steps):
|
||||
self.make_step(n_steps)
|
||||
self.render_progress()
|
||||
|
||||
def finish(self):
|
||||
self.eta_known = 0
|
||||
self.current_item = None
|
||||
self.finished = True
|
||||
|
||||
def next(self):
|
||||
if self.is_hidden:
|
||||
return next(self.iter)
|
||||
try:
|
||||
rv = next(self.iter)
|
||||
self.current_item = rv
|
||||
except StopIteration:
|
||||
self.finish()
|
||||
self.render_progress()
|
||||
raise StopIteration()
|
||||
else:
|
||||
self.update(1)
|
||||
return rv
|
||||
|
||||
if not PY2:
|
||||
__next__ = next
|
||||
del next
|
||||
|
||||
|
||||
def pager(text, color=None):
|
||||
"""Decide what method to use for paging through text."""
|
||||
stdout = _default_text_stdout()
|
||||
if not isatty(sys.stdin) or not isatty(stdout):
|
||||
return _nullpager(stdout, text, color)
|
||||
pager_cmd = (os.environ.get('PAGER', None) or '').strip()
|
||||
if pager_cmd:
|
||||
if WIN:
|
||||
return _tempfilepager(text, pager_cmd, color)
|
||||
return _pipepager(text, pager_cmd, color)
|
||||
if os.environ.get('TERM') in ('dumb', 'emacs'):
|
||||
return _nullpager(stdout, text, color)
|
||||
if WIN or sys.platform.startswith('os2'):
|
||||
return _tempfilepager(text, 'more <', color)
|
||||
if hasattr(os, 'system') and os.system('(less) 2>/dev/null') == 0:
|
||||
return _pipepager(text, 'less', color)
|
||||
|
||||
import tempfile
|
||||
fd, filename = tempfile.mkstemp()
|
||||
os.close(fd)
|
||||
try:
|
||||
if hasattr(os, 'system') and os.system('more "%s"' % filename) == 0:
|
||||
return _pipepager(text, 'more', color)
|
||||
return _nullpager(stdout, text, color)
|
||||
finally:
|
||||
os.unlink(filename)
|
||||
|
||||
|
||||
def _pipepager(text, cmd, color):
|
||||
"""Page through text by feeding it to another program. Invoking a
|
||||
pager through this might support colors.
|
||||
"""
|
||||
import subprocess
|
||||
env = dict(os.environ)
|
||||
|
||||
# If we're piping to less we might support colors under the
|
||||
# condition that
|
||||
cmd_detail = cmd.rsplit('/', 1)[-1].split()
|
||||
if color is None and cmd_detail[0] == 'less':
|
||||
less_flags = os.environ.get('LESS', '') + ' '.join(cmd_detail[1:])
|
||||
if not less_flags:
|
||||
env['LESS'] = '-R'
|
||||
color = True
|
||||
elif 'r' in less_flags or 'R' in less_flags:
|
||||
color = True
|
||||
|
||||
if not color:
|
||||
text = strip_ansi(text)
|
||||
|
||||
c = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE,
|
||||
env=env)
|
||||
encoding = get_best_encoding(c.stdin)
|
||||
try:
|
||||
c.stdin.write(text.encode(encoding, 'replace'))
|
||||
c.stdin.close()
|
||||
except (IOError, KeyboardInterrupt):
|
||||
pass
|
||||
|
||||
# Less doesn't respect ^C, but catches it for its own UI purposes (aborting
|
||||
# search or other commands inside less).
|
||||
#
|
||||
# That means when the user hits ^C, the parent process (click) terminates,
|
||||
# but less is still alive, paging the output and messing up the terminal.
|
||||
#
|
||||
# If the user wants to make the pager exit on ^C, they should set
|
||||
# `LESS='-K'`. It's not our decision to make.
|
||||
while True:
|
||||
try:
|
||||
c.wait()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
def _tempfilepager(text, cmd, color):
|
||||
"""Page through text by invoking a program on a temporary file."""
|
||||
import tempfile
|
||||
filename = tempfile.mktemp()
|
||||
if not color:
|
||||
text = strip_ansi(text)
|
||||
encoding = get_best_encoding(sys.stdout)
|
||||
with open_stream(filename, 'wb')[0] as f:
|
||||
f.write(text.encode(encoding))
|
||||
try:
|
||||
os.system(cmd + ' "' + filename + '"')
|
||||
finally:
|
||||
os.unlink(filename)
|
||||
|
||||
|
||||
def _nullpager(stream, text, color):
|
||||
"""Simply print unformatted text. This is the ultimate fallback."""
|
||||
if not color:
|
||||
text = strip_ansi(text)
|
||||
stream.write(text)
|
||||
|
||||
|
||||
class Editor(object):
|
||||
|
||||
def __init__(self, editor=None, env=None, require_save=True,
|
||||
extension='.txt'):
|
||||
self.editor = editor
|
||||
self.env = env
|
||||
self.require_save = require_save
|
||||
self.extension = extension
|
||||
|
||||
def get_editor(self):
|
||||
if self.editor is not None:
|
||||
return self.editor
|
||||
for key in 'VISUAL', 'EDITOR':
|
||||
rv = os.environ.get(key)
|
||||
if rv:
|
||||
return rv
|
||||
if WIN:
|
||||
return 'notepad'
|
||||
for editor in 'vim', 'nano':
|
||||
if os.system('which %s >/dev/null 2>&1' % editor) == 0:
|
||||
return editor
|
||||
return 'vi'
|
||||
|
||||
def edit_file(self, filename):
|
||||
import subprocess
|
||||
editor = self.get_editor()
|
||||
if self.env:
|
||||
environ = os.environ.copy()
|
||||
environ.update(self.env)
|
||||
else:
|
||||
environ = None
|
||||
try:
|
||||
c = subprocess.Popen('%s "%s"' % (editor, filename),
|
||||
env=environ, shell=True)
|
||||
exit_code = c.wait()
|
||||
if exit_code != 0:
|
||||
raise ClickException('%s: Editing failed!' % editor)
|
||||
except OSError as e:
|
||||
raise ClickException('%s: Editing failed: %s' % (editor, e))
|
||||
|
||||
def edit(self, text):
|
||||
import tempfile
|
||||
|
||||
text = text or ''
|
||||
if text and not text.endswith('\n'):
|
||||
text += '\n'
|
||||
|
||||
fd, name = tempfile.mkstemp(prefix='editor-', suffix=self.extension)
|
||||
try:
|
||||
if WIN:
|
||||
encoding = 'utf-8-sig'
|
||||
text = text.replace('\n', '\r\n')
|
||||
else:
|
||||
encoding = 'utf-8'
|
||||
text = text.encode(encoding)
|
||||
|
||||
f = os.fdopen(fd, 'wb')
|
||||
f.write(text)
|
||||
f.close()
|
||||
timestamp = os.path.getmtime(name)
|
||||
|
||||
self.edit_file(name)
|
||||
|
||||
if self.require_save \
|
||||
and os.path.getmtime(name) == timestamp:
|
||||
return None
|
||||
|
||||
f = open(name, 'rb')
|
||||
try:
|
||||
rv = f.read()
|
||||
finally:
|
||||
f.close()
|
||||
return rv.decode('utf-8-sig').replace('\r\n', '\n')
|
||||
finally:
|
||||
os.unlink(name)
|
||||
|
||||
|
||||
def open_url(url, wait=False, locate=False):
|
||||
import subprocess
|
||||
|
||||
def _unquote_file(url):
|
||||
try:
|
||||
import urllib
|
||||
except ImportError:
|
||||
import urllib
|
||||
if url.startswith('file://'):
|
||||
url = urllib.unquote(url[7:])
|
||||
return url
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
args = ['open']
|
||||
if wait:
|
||||
args.append('-W')
|
||||
if locate:
|
||||
args.append('-R')
|
||||
args.append(_unquote_file(url))
|
||||
null = open('/dev/null', 'w')
|
||||
try:
|
||||
return subprocess.Popen(args, stderr=null).wait()
|
||||
finally:
|
||||
null.close()
|
||||
elif WIN:
|
||||
if locate:
|
||||
url = _unquote_file(url)
|
||||
args = 'explorer /select,"%s"' % _unquote_file(
|
||||
url.replace('"', ''))
|
||||
else:
|
||||
args = 'start %s "" "%s"' % (
|
||||
wait and '/WAIT' or '', url.replace('"', ''))
|
||||
return os.system(args)
|
||||
|
||||
try:
|
||||
if locate:
|
||||
url = os.path.dirname(_unquote_file(url)) or '.'
|
||||
else:
|
||||
url = _unquote_file(url)
|
||||
c = subprocess.Popen(['xdg-open', url])
|
||||
if wait:
|
||||
return c.wait()
|
||||
return 0
|
||||
except OSError:
|
||||
if url.startswith(('http://', 'https://')) and not locate and not wait:
|
||||
import webbrowser
|
||||
webbrowser.open(url)
|
||||
return 0
|
||||
return 1
|
||||
|
||||
|
||||
def _translate_ch_to_exc(ch):
|
||||
if ch == '\x03':
|
||||
raise KeyboardInterrupt()
|
||||
if ch == '\x04':
|
||||
raise EOFError()
|
||||
|
||||
|
||||
if WIN:
|
||||
import msvcrt
|
||||
|
||||
def getchar(echo):
|
||||
rv = msvcrt.getch()
|
||||
if echo:
|
||||
msvcrt.putchar(rv)
|
||||
_translate_ch_to_exc(rv)
|
||||
if PY2:
|
||||
enc = getattr(sys.stdin, 'encoding', None)
|
||||
if enc is not None:
|
||||
rv = rv.decode(enc, 'replace')
|
||||
else:
|
||||
rv = rv.decode('cp1252', 'replace')
|
||||
return rv
|
||||
else:
|
||||
import tty
|
||||
import termios
|
||||
|
||||
def getchar(echo):
|
||||
if not isatty(sys.stdin):
|
||||
f = open('/dev/tty')
|
||||
fd = f.fileno()
|
||||
else:
|
||||
fd = sys.stdin.fileno()
|
||||
f = None
|
||||
try:
|
||||
old_settings = termios.tcgetattr(fd)
|
||||
try:
|
||||
tty.setraw(fd)
|
||||
ch = os.read(fd, 32)
|
||||
if echo and isatty(sys.stdout):
|
||||
sys.stdout.write(ch)
|
||||
finally:
|
||||
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
||||
sys.stdout.flush()
|
||||
if f is not None:
|
||||
f.close()
|
||||
except termios.error:
|
||||
pass
|
||||
_translate_ch_to_exc(ch)
|
||||
return ch.decode(get_best_encoding(sys.stdin), 'replace')
|
||||
@@ -1,38 +0,0 @@
|
||||
import textwrap
|
||||
from contextlib import contextmanager
|
||||
|
||||
|
||||
class TextWrapper(textwrap.TextWrapper):
|
||||
|
||||
def _handle_long_word(self, reversed_chunks, cur_line, cur_len, width):
|
||||
space_left = max(width - cur_len, 1)
|
||||
|
||||
if self.break_long_words:
|
||||
last = reversed_chunks[-1]
|
||||
cut = last[:space_left]
|
||||
res = last[space_left:]
|
||||
cur_line.append(cut)
|
||||
reversed_chunks[-1] = res
|
||||
elif not cur_line:
|
||||
cur_line.append(reversed_chunks.pop())
|
||||
|
||||
@contextmanager
|
||||
def extra_indent(self, indent):
|
||||
old_initial_indent = self.initial_indent
|
||||
old_subsequent_indent = self.subsequent_indent
|
||||
self.initial_indent += indent
|
||||
self.subsequent_indent += indent
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
self.initial_indent = old_initial_indent
|
||||
self.subsequent_indent = old_subsequent_indent
|
||||
|
||||
def indent_only(self, text):
|
||||
rv = []
|
||||
for idx, line in enumerate(text.splitlines()):
|
||||
indent = self.initial_indent
|
||||
if idx > 0:
|
||||
indent = self.subsequent_indent
|
||||
rv.append(indent + line)
|
||||
return '\n'.join(rv)
|
||||
@@ -1,118 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
import codecs
|
||||
|
||||
from ._compat import PY2
|
||||
|
||||
|
||||
# If someone wants to vendor click, we want to ensure the
|
||||
# correct package is discovered. Ideally we could use a
|
||||
# relative import here but unfortunately Python does not
|
||||
# support that.
|
||||
click = sys.modules[__name__.rsplit('.', 1)[0]]
|
||||
|
||||
|
||||
def _find_unicode_literals_frame():
|
||||
import __future__
|
||||
frm = sys._getframe(1)
|
||||
idx = 1
|
||||
while frm is not None:
|
||||
if frm.f_globals.get('__name__', '').startswith('click.'):
|
||||
frm = frm.f_back
|
||||
idx += 1
|
||||
elif frm.f_code.co_flags & __future__.unicode_literals.compiler_flag:
|
||||
return idx
|
||||
else:
|
||||
break
|
||||
return 0
|
||||
|
||||
|
||||
def _check_for_unicode_literals():
|
||||
if not __debug__:
|
||||
return
|
||||
if not PY2 or click.disable_unicode_literals_warning:
|
||||
return
|
||||
bad_frame = _find_unicode_literals_frame()
|
||||
if bad_frame <= 0:
|
||||
return
|
||||
from warnings import warn
|
||||
warn(Warning('Click detected the use of the unicode_literals '
|
||||
'__future__ import. This is heavily discouraged '
|
||||
'because it can introduce subtle bugs in your '
|
||||
'code. You should instead use explicit u"" literals '
|
||||
'for your unicode strings. For more information see '
|
||||
'http://click.pocoo.org/python3/'),
|
||||
stacklevel=bad_frame)
|
||||
|
||||
|
||||
def _verify_python3_env():
|
||||
"""Ensures that the environment is good for unicode on Python 3."""
|
||||
if PY2:
|
||||
return
|
||||
try:
|
||||
import locale
|
||||
fs_enc = codecs.lookup(locale.getpreferredencoding()).name
|
||||
except Exception:
|
||||
fs_enc = 'ascii'
|
||||
if fs_enc != 'ascii':
|
||||
return
|
||||
|
||||
extra = ''
|
||||
if os.name == 'posix':
|
||||
import subprocess
|
||||
rv = subprocess.Popen(['locale', '-a'], stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE).communicate()[0]
|
||||
good_locales = set()
|
||||
has_c_utf8 = False
|
||||
|
||||
# Make sure we're operating on text here.
|
||||
if isinstance(rv, bytes):
|
||||
rv = rv.decode('ascii', 'replace')
|
||||
|
||||
for line in rv.splitlines():
|
||||
locale = line.strip()
|
||||
if locale.lower().endswith(('.utf-8', '.utf8')):
|
||||
good_locales.add(locale)
|
||||
if locale.lower() in ('c.utf8', 'c.utf-8'):
|
||||
has_c_utf8 = True
|
||||
|
||||
extra += '\n\n'
|
||||
if not good_locales:
|
||||
extra += (
|
||||
'Additional information: on this system no suitable UTF-8\n'
|
||||
'locales were discovered. This most likely requires resolving\n'
|
||||
'by reconfiguring the locale system.'
|
||||
)
|
||||
elif has_c_utf8:
|
||||
extra += (
|
||||
'This system supports the C.UTF-8 locale which is recommended.\n'
|
||||
'You might be able to resolve your issue by exporting the\n'
|
||||
'following environment variables:\n\n'
|
||||
' export LC_ALL=C.UTF-8\n'
|
||||
' export LANG=C.UTF-8'
|
||||
)
|
||||
else:
|
||||
extra += (
|
||||
'This system lists a couple of UTF-8 supporting locales that\n'
|
||||
'you can pick from. The following suitable locales where\n'
|
||||
'discovered: %s'
|
||||
) % ', '.join(sorted(good_locales))
|
||||
|
||||
bad_locale = None
|
||||
for locale in os.environ.get('LC_ALL'), os.environ.get('LANG'):
|
||||
if locale and locale.lower().endswith(('.utf-8', '.utf8')):
|
||||
bad_locale = locale
|
||||
if locale is not None:
|
||||
break
|
||||
if bad_locale is not None:
|
||||
extra += (
|
||||
'\n\nClick discovered that you exported a UTF-8 locale\n'
|
||||
'but the locale system could not pick up from it because\n'
|
||||
'it does not exist. The exported locale is "%s" but it\n'
|
||||
'is not supported'
|
||||
) % bad_locale
|
||||
|
||||
raise RuntimeError('Click will abort further execution because Python 3 '
|
||||
'was configured to use ASCII as encoding for the '
|
||||
'environment. Consult http://click.pocoo.org/python3/'
|
||||
'for mitigation steps.' + extra)
|
||||
@@ -1,273 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# This module is based on the excellent work by Adam Bartoš who
|
||||
# provided a lot of what went into the implementation here in
|
||||
# the discussion to issue1602 in the Python bug tracker.
|
||||
#
|
||||
# There are some general differences in regards to how this works
|
||||
# compared to the original patches as we do not need to patch
|
||||
# the entire interpreter but just work in our little world of
|
||||
# echo and prmopt.
|
||||
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
import zlib
|
||||
import time
|
||||
import ctypes
|
||||
import msvcrt
|
||||
from click._compat import _NonClosingTextIOWrapper, text_type, PY2
|
||||
from ctypes import byref, POINTER, c_int, c_char, c_char_p, \
|
||||
c_void_p, py_object, c_ssize_t, c_ulong, windll, WINFUNCTYPE
|
||||
try:
|
||||
from ctypes import pythonapi
|
||||
PyObject_GetBuffer = pythonapi.PyObject_GetBuffer
|
||||
PyBuffer_Release = pythonapi.PyBuffer_Release
|
||||
except ImportError:
|
||||
pythonapi = None
|
||||
from ctypes.wintypes import LPWSTR, LPCWSTR
|
||||
|
||||
|
||||
c_ssize_p = POINTER(c_ssize_t)
|
||||
|
||||
kernel32 = windll.kernel32
|
||||
GetStdHandle = kernel32.GetStdHandle
|
||||
ReadConsoleW = kernel32.ReadConsoleW
|
||||
WriteConsoleW = kernel32.WriteConsoleW
|
||||
GetLastError = kernel32.GetLastError
|
||||
GetCommandLineW = WINFUNCTYPE(LPWSTR)(
|
||||
('GetCommandLineW', windll.kernel32))
|
||||
CommandLineToArgvW = WINFUNCTYPE(
|
||||
POINTER(LPWSTR), LPCWSTR, POINTER(c_int))(
|
||||
('CommandLineToArgvW', windll.shell32))
|
||||
|
||||
|
||||
STDIN_HANDLE = GetStdHandle(-10)
|
||||
STDOUT_HANDLE = GetStdHandle(-11)
|
||||
STDERR_HANDLE = GetStdHandle(-12)
|
||||
|
||||
|
||||
PyBUF_SIMPLE = 0
|
||||
PyBUF_WRITABLE = 1
|
||||
|
||||
ERROR_SUCCESS = 0
|
||||
ERROR_NOT_ENOUGH_MEMORY = 8
|
||||
ERROR_OPERATION_ABORTED = 995
|
||||
|
||||
STDIN_FILENO = 0
|
||||
STDOUT_FILENO = 1
|
||||
STDERR_FILENO = 2
|
||||
|
||||
EOF = b'\x1a'
|
||||
MAX_BYTES_WRITTEN = 32767
|
||||
|
||||
|
||||
class Py_buffer(ctypes.Structure):
|
||||
_fields_ = [
|
||||
('buf', c_void_p),
|
||||
('obj', py_object),
|
||||
('len', c_ssize_t),
|
||||
('itemsize', c_ssize_t),
|
||||
('readonly', c_int),
|
||||
('ndim', c_int),
|
||||
('format', c_char_p),
|
||||
('shape', c_ssize_p),
|
||||
('strides', c_ssize_p),
|
||||
('suboffsets', c_ssize_p),
|
||||
('internal', c_void_p)
|
||||
]
|
||||
|
||||
if PY2:
|
||||
_fields_.insert(-1, ('smalltable', c_ssize_t * 2))
|
||||
|
||||
|
||||
# On PyPy we cannot get buffers so our ability to operate here is
|
||||
# serverly limited.
|
||||
if pythonapi is None:
|
||||
get_buffer = None
|
||||
else:
|
||||
def get_buffer(obj, writable=False):
|
||||
buf = Py_buffer()
|
||||
flags = PyBUF_WRITABLE if writable else PyBUF_SIMPLE
|
||||
PyObject_GetBuffer(py_object(obj), byref(buf), flags)
|
||||
try:
|
||||
buffer_type = c_char * buf.len
|
||||
return buffer_type.from_address(buf.buf)
|
||||
finally:
|
||||
PyBuffer_Release(byref(buf))
|
||||
|
||||
|
||||
class _WindowsConsoleRawIOBase(io.RawIOBase):
|
||||
|
||||
def __init__(self, handle):
|
||||
self.handle = handle
|
||||
|
||||
def isatty(self):
|
||||
io.RawIOBase.isatty(self)
|
||||
return True
|
||||
|
||||
|
||||
class _WindowsConsoleReader(_WindowsConsoleRawIOBase):
|
||||
|
||||
def readable(self):
|
||||
return True
|
||||
|
||||
def readinto(self, b):
|
||||
bytes_to_be_read = len(b)
|
||||
if not bytes_to_be_read:
|
||||
return 0
|
||||
elif bytes_to_be_read % 2:
|
||||
raise ValueError('cannot read odd number of bytes from '
|
||||
'UTF-16-LE encoded console')
|
||||
|
||||
buffer = get_buffer(b, writable=True)
|
||||
code_units_to_be_read = bytes_to_be_read // 2
|
||||
code_units_read = c_ulong()
|
||||
|
||||
rv = ReadConsoleW(self.handle, buffer, code_units_to_be_read,
|
||||
byref(code_units_read), None)
|
||||
if GetLastError() == ERROR_OPERATION_ABORTED:
|
||||
# wait for KeyboardInterrupt
|
||||
time.sleep(0.1)
|
||||
if not rv:
|
||||
raise OSError('Windows error: %s' % GetLastError())
|
||||
|
||||
if buffer[0] == EOF:
|
||||
return 0
|
||||
return 2 * code_units_read.value
|
||||
|
||||
|
||||
class _WindowsConsoleWriter(_WindowsConsoleRawIOBase):
|
||||
|
||||
def writable(self):
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def _get_error_message(errno):
|
||||
if errno == ERROR_SUCCESS:
|
||||
return 'ERROR_SUCCESS'
|
||||
elif errno == ERROR_NOT_ENOUGH_MEMORY:
|
||||
return 'ERROR_NOT_ENOUGH_MEMORY'
|
||||
return 'Windows error %s' % errno
|
||||
|
||||
def write(self, b):
|
||||
bytes_to_be_written = len(b)
|
||||
buf = get_buffer(b)
|
||||
code_units_to_be_written = min(bytes_to_be_written,
|
||||
MAX_BYTES_WRITTEN) // 2
|
||||
code_units_written = c_ulong()
|
||||
|
||||
WriteConsoleW(self.handle, buf, code_units_to_be_written,
|
||||
byref(code_units_written), None)
|
||||
bytes_written = 2 * code_units_written.value
|
||||
|
||||
if bytes_written == 0 and bytes_to_be_written > 0:
|
||||
raise OSError(self._get_error_message(GetLastError()))
|
||||
return bytes_written
|
||||
|
||||
|
||||
class ConsoleStream(object):
|
||||
|
||||
def __init__(self, text_stream, byte_stream):
|
||||
self._text_stream = text_stream
|
||||
self.buffer = byte_stream
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.buffer.name
|
||||
|
||||
def write(self, x):
|
||||
if isinstance(x, text_type):
|
||||
return self._text_stream.write(x)
|
||||
try:
|
||||
self.flush()
|
||||
except Exception:
|
||||
pass
|
||||
return self.buffer.write(x)
|
||||
|
||||
def writelines(self, lines):
|
||||
for line in lines:
|
||||
self.write(line)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self._text_stream, name)
|
||||
|
||||
def isatty(self):
|
||||
return self.buffer.isatty()
|
||||
|
||||
def __repr__(self):
|
||||
return '<ConsoleStream name=%r encoding=%r>' % (
|
||||
self.name,
|
||||
self.encoding,
|
||||
)
|
||||
|
||||
|
||||
def _get_text_stdin(buffer_stream):
|
||||
text_stream = _NonClosingTextIOWrapper(
|
||||
io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)),
|
||||
'utf-16-le', 'strict', line_buffering=True)
|
||||
return ConsoleStream(text_stream, buffer_stream)
|
||||
|
||||
|
||||
def _get_text_stdout(buffer_stream):
|
||||
text_stream = _NonClosingTextIOWrapper(
|
||||
_WindowsConsoleWriter(STDOUT_HANDLE),
|
||||
'utf-16-le', 'strict', line_buffering=True)
|
||||
return ConsoleStream(text_stream, buffer_stream)
|
||||
|
||||
|
||||
def _get_text_stderr(buffer_stream):
|
||||
text_stream = _NonClosingTextIOWrapper(
|
||||
_WindowsConsoleWriter(STDERR_HANDLE),
|
||||
'utf-16-le', 'strict', line_buffering=True)
|
||||
return ConsoleStream(text_stream, buffer_stream)
|
||||
|
||||
|
||||
if PY2:
|
||||
def _hash_py_argv():
|
||||
return zlib.crc32('\x00'.join(sys.argv[1:]))
|
||||
|
||||
_initial_argv_hash = _hash_py_argv()
|
||||
|
||||
def _get_windows_argv():
|
||||
argc = c_int(0)
|
||||
argv_unicode = CommandLineToArgvW(GetCommandLineW(), byref(argc))
|
||||
argv = [argv_unicode[i] for i in range(0, argc.value)]
|
||||
|
||||
if not hasattr(sys, 'frozen'):
|
||||
argv = argv[1:]
|
||||
while len(argv) > 0:
|
||||
arg = argv[0]
|
||||
if not arg.startswith('-') or arg == '-':
|
||||
break
|
||||
argv = argv[1:]
|
||||
if arg.startswith(('-c', '-m')):
|
||||
break
|
||||
|
||||
return argv[1:]
|
||||
|
||||
|
||||
_stream_factories = {
|
||||
0: _get_text_stdin,
|
||||
1: _get_text_stdout,
|
||||
2: _get_text_stderr,
|
||||
}
|
||||
|
||||
|
||||
def _get_windows_console_stream(f, encoding, errors):
|
||||
if get_buffer is not None and \
|
||||
encoding in ('utf-16-le', None) \
|
||||
and errors in ('strict', None) and \
|
||||
hasattr(f, 'isatty') and f.isatty():
|
||||
func = _stream_factories.get(f.fileno())
|
||||
if func is not None:
|
||||
if not PY2:
|
||||
f = getattr(f, 'buffer')
|
||||
if f is None:
|
||||
return None
|
||||
else:
|
||||
# If we are on Python 2 we need to set the stream that we
|
||||
# deal with to binary mode as otherwise the exercise if a
|
||||
# bit moot. The same problems apply as for
|
||||
# get_binary_stdin and friends from _compat.
|
||||
msvcrt.setmode(f.fileno(), os.O_BINARY)
|
||||
return func(f)
|
||||
1744
click/core.py
1744
click/core.py
File diff suppressed because it is too large
Load Diff
@@ -1,304 +0,0 @@
|
||||
import sys
|
||||
import inspect
|
||||
|
||||
from functools import update_wrapper
|
||||
|
||||
from ._compat import iteritems
|
||||
from ._unicodefun import _check_for_unicode_literals
|
||||
from .utils import echo
|
||||
from .globals import get_current_context
|
||||
|
||||
|
||||
def pass_context(f):
|
||||
"""Marks a callback as wanting to receive the current context
|
||||
object as first argument.
|
||||
"""
|
||||
def new_func(*args, **kwargs):
|
||||
return f(get_current_context(), *args, **kwargs)
|
||||
return update_wrapper(new_func, f)
|
||||
|
||||
|
||||
def pass_obj(f):
|
||||
"""Similar to :func:`pass_context`, but only pass the object on the
|
||||
context onwards (:attr:`Context.obj`). This is useful if that object
|
||||
represents the state of a nested system.
|
||||
"""
|
||||
def new_func(*args, **kwargs):
|
||||
return f(get_current_context().obj, *args, **kwargs)
|
||||
return update_wrapper(new_func, f)
|
||||
|
||||
|
||||
def make_pass_decorator(object_type, ensure=False):
|
||||
"""Given an object type this creates a decorator that will work
|
||||
similar to :func:`pass_obj` but instead of passing the object of the
|
||||
current context, it will find the innermost context of type
|
||||
:func:`object_type`.
|
||||
|
||||
This generates a decorator that works roughly like this::
|
||||
|
||||
from functools import update_wrapper
|
||||
|
||||
def decorator(f):
|
||||
@pass_context
|
||||
def new_func(ctx, *args, **kwargs):
|
||||
obj = ctx.find_object(object_type)
|
||||
return ctx.invoke(f, obj, *args, **kwargs)
|
||||
return update_wrapper(new_func, f)
|
||||
return decorator
|
||||
|
||||
:param object_type: the type of the object to pass.
|
||||
:param ensure: if set to `True`, a new object will be created and
|
||||
remembered on the context if it's not there yet.
|
||||
"""
|
||||
def decorator(f):
|
||||
def new_func(*args, **kwargs):
|
||||
ctx = get_current_context()
|
||||
if ensure:
|
||||
obj = ctx.ensure_object(object_type)
|
||||
else:
|
||||
obj = ctx.find_object(object_type)
|
||||
if obj is None:
|
||||
raise RuntimeError('Managed to invoke callback without a '
|
||||
'context object of type %r existing'
|
||||
% object_type.__name__)
|
||||
return ctx.invoke(f, obj, *args[1:], **kwargs)
|
||||
return update_wrapper(new_func, f)
|
||||
return decorator
|
||||
|
||||
|
||||
def _make_command(f, name, attrs, cls):
|
||||
if isinstance(f, Command):
|
||||
raise TypeError('Attempted to convert a callback into a '
|
||||
'command twice.')
|
||||
try:
|
||||
params = f.__click_params__
|
||||
params.reverse()
|
||||
del f.__click_params__
|
||||
except AttributeError:
|
||||
params = []
|
||||
help = attrs.get('help')
|
||||
if help is None:
|
||||
help = inspect.getdoc(f)
|
||||
if isinstance(help, bytes):
|
||||
help = help.decode('utf-8')
|
||||
else:
|
||||
help = inspect.cleandoc(help)
|
||||
attrs['help'] = help
|
||||
_check_for_unicode_literals()
|
||||
return cls(name=name or f.__name__.lower(),
|
||||
callback=f, params=params, **attrs)
|
||||
|
||||
|
||||
def command(name=None, cls=None, **attrs):
|
||||
"""Creates a new :class:`Command` and uses the decorated function as
|
||||
callback. This will also automatically attach all decorated
|
||||
:func:`option`\s and :func:`argument`\s as parameters to the command.
|
||||
|
||||
The name of the command defaults to the name of the function. If you
|
||||
want to change that, you can pass the intended name as the first
|
||||
argument.
|
||||
|
||||
All keyword arguments are forwarded to the underlying command class.
|
||||
|
||||
Once decorated the function turns into a :class:`Command` instance
|
||||
that can be invoked as a command line utility or be attached to a
|
||||
command :class:`Group`.
|
||||
|
||||
:param name: the name of the command. This defaults to the function
|
||||
name.
|
||||
:param cls: the command class to instantiate. This defaults to
|
||||
:class:`Command`.
|
||||
"""
|
||||
if cls is None:
|
||||
cls = Command
|
||||
def decorator(f):
|
||||
cmd = _make_command(f, name, attrs, cls)
|
||||
cmd.__doc__ = f.__doc__
|
||||
return cmd
|
||||
return decorator
|
||||
|
||||
|
||||
def group(name=None, **attrs):
|
||||
"""Creates a new :class:`Group` with a function as callback. This
|
||||
works otherwise the same as :func:`command` just that the `cls`
|
||||
parameter is set to :class:`Group`.
|
||||
"""
|
||||
attrs.setdefault('cls', Group)
|
||||
return command(name, **attrs)
|
||||
|
||||
|
||||
def _param_memo(f, param):
|
||||
if isinstance(f, Command):
|
||||
f.params.append(param)
|
||||
else:
|
||||
if not hasattr(f, '__click_params__'):
|
||||
f.__click_params__ = []
|
||||
f.__click_params__.append(param)
|
||||
|
||||
|
||||
def argument(*param_decls, **attrs):
|
||||
"""Attaches an argument to the command. All positional arguments are
|
||||
passed as parameter declarations to :class:`Argument`; all keyword
|
||||
arguments are forwarded unchanged (except ``cls``).
|
||||
This is equivalent to creating an :class:`Argument` instance manually
|
||||
and attaching it to the :attr:`Command.params` list.
|
||||
|
||||
:param cls: the argument class to instantiate. This defaults to
|
||||
:class:`Argument`.
|
||||
"""
|
||||
def decorator(f):
|
||||
ArgumentClass = attrs.pop('cls', Argument)
|
||||
_param_memo(f, ArgumentClass(param_decls, **attrs))
|
||||
return f
|
||||
return decorator
|
||||
|
||||
|
||||
def option(*param_decls, **attrs):
|
||||
"""Attaches an option to the command. All positional arguments are
|
||||
passed as parameter declarations to :class:`Option`; all keyword
|
||||
arguments are forwarded unchanged (except ``cls``).
|
||||
This is equivalent to creating an :class:`Option` instance manually
|
||||
and attaching it to the :attr:`Command.params` list.
|
||||
|
||||
:param cls: the option class to instantiate. This defaults to
|
||||
:class:`Option`.
|
||||
"""
|
||||
def decorator(f):
|
||||
if 'help' in attrs:
|
||||
attrs['help'] = inspect.cleandoc(attrs['help'])
|
||||
OptionClass = attrs.pop('cls', Option)
|
||||
_param_memo(f, OptionClass(param_decls, **attrs))
|
||||
return f
|
||||
return decorator
|
||||
|
||||
|
||||
def confirmation_option(*param_decls, **attrs):
|
||||
"""Shortcut for confirmation prompts that can be ignored by passing
|
||||
``--yes`` as parameter.
|
||||
|
||||
This is equivalent to decorating a function with :func:`option` with
|
||||
the following parameters::
|
||||
|
||||
def callback(ctx, param, value):
|
||||
if not value:
|
||||
ctx.abort()
|
||||
|
||||
@click.command()
|
||||
@click.option('--yes', is_flag=True, callback=callback,
|
||||
expose_value=False, prompt='Do you want to continue?')
|
||||
def dropdb():
|
||||
pass
|
||||
"""
|
||||
def decorator(f):
|
||||
def callback(ctx, param, value):
|
||||
if not value:
|
||||
ctx.abort()
|
||||
attrs.setdefault('is_flag', True)
|
||||
attrs.setdefault('callback', callback)
|
||||
attrs.setdefault('expose_value', False)
|
||||
attrs.setdefault('prompt', 'Do you want to continue?')
|
||||
attrs.setdefault('help', 'Confirm the action without prompting.')
|
||||
return option(*(param_decls or ('--yes',)), **attrs)(f)
|
||||
return decorator
|
||||
|
||||
|
||||
def password_option(*param_decls, **attrs):
|
||||
"""Shortcut for password prompts.
|
||||
|
||||
This is equivalent to decorating a function with :func:`option` with
|
||||
the following parameters::
|
||||
|
||||
@click.command()
|
||||
@click.option('--password', prompt=True, confirmation_prompt=True,
|
||||
hide_input=True)
|
||||
def changeadmin(password):
|
||||
pass
|
||||
"""
|
||||
def decorator(f):
|
||||
attrs.setdefault('prompt', True)
|
||||
attrs.setdefault('confirmation_prompt', True)
|
||||
attrs.setdefault('hide_input', True)
|
||||
return option(*(param_decls or ('--password',)), **attrs)(f)
|
||||
return decorator
|
||||
|
||||
|
||||
def version_option(version=None, *param_decls, **attrs):
|
||||
"""Adds a ``--version`` option which immediately ends the program
|
||||
printing out the version number. This is implemented as an eager
|
||||
option that prints the version and exits the program in the callback.
|
||||
|
||||
:param version: the version number to show. If not provided Click
|
||||
attempts an auto discovery via setuptools.
|
||||
:param prog_name: the name of the program (defaults to autodetection)
|
||||
:param message: custom message to show instead of the default
|
||||
(``'%(prog)s, version %(version)s'``)
|
||||
:param others: everything else is forwarded to :func:`option`.
|
||||
"""
|
||||
if version is None:
|
||||
module = sys._getframe(1).f_globals.get('__name__')
|
||||
def decorator(f):
|
||||
prog_name = attrs.pop('prog_name', None)
|
||||
message = attrs.pop('message', '%(prog)s, version %(version)s')
|
||||
|
||||
def callback(ctx, param, value):
|
||||
if not value or ctx.resilient_parsing:
|
||||
return
|
||||
prog = prog_name
|
||||
if prog is None:
|
||||
prog = ctx.find_root().info_name
|
||||
ver = version
|
||||
if ver is None:
|
||||
try:
|
||||
import pkg_resources
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
for dist in pkg_resources.working_set:
|
||||
scripts = dist.get_entry_map().get('console_scripts') or {}
|
||||
for script_name, entry_point in iteritems(scripts):
|
||||
if entry_point.module_name == module:
|
||||
ver = dist.version
|
||||
break
|
||||
if ver is None:
|
||||
raise RuntimeError('Could not determine version')
|
||||
echo(message % {
|
||||
'prog': prog,
|
||||
'version': ver,
|
||||
}, color=ctx.color)
|
||||
ctx.exit()
|
||||
|
||||
attrs.setdefault('is_flag', True)
|
||||
attrs.setdefault('expose_value', False)
|
||||
attrs.setdefault('is_eager', True)
|
||||
attrs.setdefault('help', 'Show the version and exit.')
|
||||
attrs['callback'] = callback
|
||||
return option(*(param_decls or ('--version',)), **attrs)(f)
|
||||
return decorator
|
||||
|
||||
|
||||
def help_option(*param_decls, **attrs):
|
||||
"""Adds a ``--help`` option which immediately ends the program
|
||||
printing out the help page. This is usually unnecessary to add as
|
||||
this is added by default to all commands unless suppressed.
|
||||
|
||||
Like :func:`version_option`, this is implemented as eager option that
|
||||
prints in the callback and exits.
|
||||
|
||||
All arguments are forwarded to :func:`option`.
|
||||
"""
|
||||
def decorator(f):
|
||||
def callback(ctx, param, value):
|
||||
if value and not ctx.resilient_parsing:
|
||||
echo(ctx.get_help(), color=ctx.color)
|
||||
ctx.exit()
|
||||
attrs.setdefault('is_flag', True)
|
||||
attrs.setdefault('expose_value', False)
|
||||
attrs.setdefault('help', 'Show this message and exit.')
|
||||
attrs.setdefault('is_eager', True)
|
||||
attrs['callback'] = callback
|
||||
return option(*(param_decls or ('--help',)), **attrs)(f)
|
||||
return decorator
|
||||
|
||||
|
||||
# Circular dependencies between core and decorators
|
||||
from .core import Command, Group, Argument, Option
|
||||
@@ -1,201 +0,0 @@
|
||||
from ._compat import PY2, filename_to_ui, get_text_stderr
|
||||
from .utils import echo
|
||||
|
||||
|
||||
class ClickException(Exception):
|
||||
"""An exception that Click can handle and show to the user."""
|
||||
|
||||
#: The exit code for this exception
|
||||
exit_code = 1
|
||||
|
||||
def __init__(self, message):
|
||||
if PY2:
|
||||
if message is not None:
|
||||
message = message.encode('utf-8')
|
||||
Exception.__init__(self, message)
|
||||
self.message = message
|
||||
|
||||
def format_message(self):
|
||||
return self.message
|
||||
|
||||
def show(self, file=None):
|
||||
if file is None:
|
||||
file = get_text_stderr()
|
||||
echo('Error: %s' % self.format_message(), file=file)
|
||||
|
||||
|
||||
class UsageError(ClickException):
|
||||
"""An internal exception that signals a usage error. This typically
|
||||
aborts any further handling.
|
||||
|
||||
:param message: the error message to display.
|
||||
:param ctx: optionally the context that caused this error. Click will
|
||||
fill in the context automatically in some situations.
|
||||
"""
|
||||
exit_code = 2
|
||||
|
||||
def __init__(self, message, ctx=None):
|
||||
ClickException.__init__(self, message)
|
||||
self.ctx = ctx
|
||||
|
||||
def show(self, file=None):
|
||||
if file is None:
|
||||
file = get_text_stderr()
|
||||
color = None
|
||||
if self.ctx is not None:
|
||||
color = self.ctx.color
|
||||
echo(self.ctx.get_usage() + '\n', file=file, color=color)
|
||||
echo('Error: %s' % self.format_message(), file=file, color=color)
|
||||
|
||||
|
||||
class BadParameter(UsageError):
|
||||
"""An exception that formats out a standardized error message for a
|
||||
bad parameter. This is useful when thrown from a callback or type as
|
||||
Click will attach contextual information to it (for instance, which
|
||||
parameter it is).
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
:param param: the parameter object that caused this error. This can
|
||||
be left out, and Click will attach this info itself
|
||||
if possible.
|
||||
:param param_hint: a string that shows up as parameter name. This
|
||||
can be used as alternative to `param` in cases
|
||||
where custom validation should happen. If it is
|
||||
a string it's used as such, if it's a list then
|
||||
each item is quoted and separated.
|
||||
"""
|
||||
|
||||
def __init__(self, message, ctx=None, param=None,
|
||||
param_hint=None):
|
||||
UsageError.__init__(self, message, ctx)
|
||||
self.param = param
|
||||
self.param_hint = param_hint
|
||||
|
||||
def format_message(self):
|
||||
if self.param_hint is not None:
|
||||
param_hint = self.param_hint
|
||||
elif self.param is not None:
|
||||
param_hint = self.param.opts or [self.param.human_readable_name]
|
||||
else:
|
||||
return 'Invalid value: %s' % self.message
|
||||
if isinstance(param_hint, (tuple, list)):
|
||||
param_hint = ' / '.join('"%s"' % x for x in param_hint)
|
||||
return 'Invalid value for %s: %s' % (param_hint, self.message)
|
||||
|
||||
|
||||
class MissingParameter(BadParameter):
|
||||
"""Raised if click required an option or argument but it was not
|
||||
provided when invoking the script.
|
||||
|
||||
.. versionadded:: 4.0
|
||||
|
||||
:param param_type: a string that indicates the type of the parameter.
|
||||
The default is to inherit the parameter type from
|
||||
the given `param`. Valid values are ``'parameter'``,
|
||||
``'option'`` or ``'argument'``.
|
||||
"""
|
||||
|
||||
def __init__(self, message=None, ctx=None, param=None,
|
||||
param_hint=None, param_type=None):
|
||||
BadParameter.__init__(self, message, ctx, param, param_hint)
|
||||
self.param_type = param_type
|
||||
|
||||
def format_message(self):
|
||||
if self.param_hint is not None:
|
||||
param_hint = self.param_hint
|
||||
elif self.param is not None:
|
||||
param_hint = self.param.opts or [self.param.human_readable_name]
|
||||
else:
|
||||
param_hint = None
|
||||
if isinstance(param_hint, (tuple, list)):
|
||||
param_hint = ' / '.join('"%s"' % x for x in param_hint)
|
||||
|
||||
param_type = self.param_type
|
||||
if param_type is None and self.param is not None:
|
||||
param_type = self.param.param_type_name
|
||||
|
||||
msg = self.message
|
||||
if self.param is not None:
|
||||
msg_extra = self.param.type.get_missing_message(self.param)
|
||||
if msg_extra:
|
||||
if msg:
|
||||
msg += '. ' + msg_extra
|
||||
else:
|
||||
msg = msg_extra
|
||||
|
||||
return 'Missing %s%s%s%s' % (
|
||||
param_type,
|
||||
param_hint and ' %s' % param_hint or '',
|
||||
msg and '. ' or '.',
|
||||
msg or '',
|
||||
)
|
||||
|
||||
|
||||
class NoSuchOption(UsageError):
|
||||
"""Raised if click attempted to handle an option that does not
|
||||
exist.
|
||||
|
||||
.. versionadded:: 4.0
|
||||
"""
|
||||
|
||||
def __init__(self, option_name, message=None, possibilities=None,
|
||||
ctx=None):
|
||||
if message is None:
|
||||
message = 'no such option: %s' % option_name
|
||||
UsageError.__init__(self, message, ctx)
|
||||
self.option_name = option_name
|
||||
self.possibilities = possibilities
|
||||
|
||||
def format_message(self):
|
||||
bits = [self.message]
|
||||
if self.possibilities:
|
||||
if len(self.possibilities) == 1:
|
||||
bits.append('Did you mean %s?' % self.possibilities[0])
|
||||
else:
|
||||
possibilities = sorted(self.possibilities)
|
||||
bits.append('(Possible options: %s)' % ', '.join(possibilities))
|
||||
return ' '.join(bits)
|
||||
|
||||
|
||||
class BadOptionUsage(UsageError):
|
||||
"""Raised if an option is generally supplied but the use of the option
|
||||
was incorrect. This is for instance raised if the number of arguments
|
||||
for an option is not correct.
|
||||
|
||||
.. versionadded:: 4.0
|
||||
"""
|
||||
|
||||
def __init__(self, message, ctx=None):
|
||||
UsageError.__init__(self, message, ctx)
|
||||
|
||||
|
||||
class BadArgumentUsage(UsageError):
|
||||
"""Raised if an argument is generally supplied but the use of the argument
|
||||
was incorrect. This is for instance raised if the number of values
|
||||
for an argument is not correct.
|
||||
|
||||
.. versionadded:: 6.0
|
||||
"""
|
||||
|
||||
def __init__(self, message, ctx=None):
|
||||
UsageError.__init__(self, message, ctx)
|
||||
|
||||
|
||||
class FileError(ClickException):
|
||||
"""Raised if a file cannot be opened."""
|
||||
|
||||
def __init__(self, filename, hint=None):
|
||||
ui_filename = filename_to_ui(filename)
|
||||
if hint is None:
|
||||
hint = 'unknown error'
|
||||
ClickException.__init__(self, hint)
|
||||
self.ui_filename = ui_filename
|
||||
self.filename = filename
|
||||
|
||||
def format_message(self):
|
||||
return 'Could not open file %s: %s' % (self.ui_filename, self.message)
|
||||
|
||||
|
||||
class Abort(RuntimeError):
|
||||
"""An internal signalling exception that signals Click to abort."""
|
||||
@@ -1,256 +0,0 @@
|
||||
from contextlib import contextmanager
|
||||
from .termui import get_terminal_size
|
||||
from .parser import split_opt
|
||||
from ._compat import term_len
|
||||
|
||||
|
||||
# Can force a width. This is used by the test system
|
||||
FORCED_WIDTH = None
|
||||
|
||||
|
||||
def measure_table(rows):
|
||||
widths = {}
|
||||
for row in rows:
|
||||
for idx, col in enumerate(row):
|
||||
widths[idx] = max(widths.get(idx, 0), term_len(col))
|
||||
return tuple(y for x, y in sorted(widths.items()))
|
||||
|
||||
|
||||
def iter_rows(rows, col_count):
|
||||
for row in rows:
|
||||
row = tuple(row)
|
||||
yield row + ('',) * (col_count - len(row))
|
||||
|
||||
|
||||
def wrap_text(text, width=78, initial_indent='', subsequent_indent='',
|
||||
preserve_paragraphs=False):
|
||||
"""A helper function that intelligently wraps text. By default, it
|
||||
assumes that it operates on a single paragraph of text but if the
|
||||
`preserve_paragraphs` parameter is provided it will intelligently
|
||||
handle paragraphs (defined by two empty lines).
|
||||
|
||||
If paragraphs are handled, a paragraph can be prefixed with an empty
|
||||
line containing the ``\\b`` character (``\\x08``) to indicate that
|
||||
no rewrapping should happen in that block.
|
||||
|
||||
:param text: the text that should be rewrapped.
|
||||
:param width: the maximum width for the text.
|
||||
:param initial_indent: the initial indent that should be placed on the
|
||||
first line as a string.
|
||||
:param subsequent_indent: the indent string that should be placed on
|
||||
each consecutive line.
|
||||
:param preserve_paragraphs: if this flag is set then the wrapping will
|
||||
intelligently handle paragraphs.
|
||||
"""
|
||||
from ._textwrap import TextWrapper
|
||||
text = text.expandtabs()
|
||||
wrapper = TextWrapper(width, initial_indent=initial_indent,
|
||||
subsequent_indent=subsequent_indent,
|
||||
replace_whitespace=False)
|
||||
if not preserve_paragraphs:
|
||||
return wrapper.fill(text)
|
||||
|
||||
p = []
|
||||
buf = []
|
||||
indent = None
|
||||
|
||||
def _flush_par():
|
||||
if not buf:
|
||||
return
|
||||
if buf[0].strip() == '\b':
|
||||
p.append((indent or 0, True, '\n'.join(buf[1:])))
|
||||
else:
|
||||
p.append((indent or 0, False, ' '.join(buf)))
|
||||
del buf[:]
|
||||
|
||||
for line in text.splitlines():
|
||||
if not line:
|
||||
_flush_par()
|
||||
indent = None
|
||||
else:
|
||||
if indent is None:
|
||||
orig_len = term_len(line)
|
||||
line = line.lstrip()
|
||||
indent = orig_len - term_len(line)
|
||||
buf.append(line)
|
||||
_flush_par()
|
||||
|
||||
rv = []
|
||||
for indent, raw, text in p:
|
||||
with wrapper.extra_indent(' ' * indent):
|
||||
if raw:
|
||||
rv.append(wrapper.indent_only(text))
|
||||
else:
|
||||
rv.append(wrapper.fill(text))
|
||||
|
||||
return '\n\n'.join(rv)
|
||||
|
||||
|
||||
class HelpFormatter(object):
|
||||
"""This class helps with formatting text-based help pages. It's
|
||||
usually just needed for very special internal cases, but it's also
|
||||
exposed so that developers can write their own fancy outputs.
|
||||
|
||||
At present, it always writes into memory.
|
||||
|
||||
:param indent_increment: the additional increment for each level.
|
||||
:param width: the width for the text. This defaults to the terminal
|
||||
width clamped to a maximum of 78.
|
||||
"""
|
||||
|
||||
def __init__(self, indent_increment=2, width=None, max_width=None):
|
||||
self.indent_increment = indent_increment
|
||||
if max_width is None:
|
||||
max_width = 80
|
||||
if width is None:
|
||||
width = FORCED_WIDTH
|
||||
if width is None:
|
||||
width = max(min(get_terminal_size()[0], max_width) - 2, 50)
|
||||
self.width = width
|
||||
self.current_indent = 0
|
||||
self.buffer = []
|
||||
|
||||
def write(self, string):
|
||||
"""Writes a unicode string into the internal buffer."""
|
||||
self.buffer.append(string)
|
||||
|
||||
def indent(self):
|
||||
"""Increases the indentation."""
|
||||
self.current_indent += self.indent_increment
|
||||
|
||||
def dedent(self):
|
||||
"""Decreases the indentation."""
|
||||
self.current_indent -= self.indent_increment
|
||||
|
||||
def write_usage(self, prog, args='', prefix='Usage: '):
|
||||
"""Writes a usage line into the buffer.
|
||||
|
||||
:param prog: the program name.
|
||||
:param args: whitespace separated list of arguments.
|
||||
:param prefix: the prefix for the first line.
|
||||
"""
|
||||
usage_prefix = '%*s%s ' % (self.current_indent, prefix, prog)
|
||||
text_width = self.width - self.current_indent
|
||||
|
||||
if text_width >= (term_len(usage_prefix) + 20):
|
||||
# The arguments will fit to the right of the prefix.
|
||||
indent = ' ' * term_len(usage_prefix)
|
||||
self.write(wrap_text(args, text_width,
|
||||
initial_indent=usage_prefix,
|
||||
subsequent_indent=indent))
|
||||
else:
|
||||
# The prefix is too long, put the arguments on the next line.
|
||||
self.write(usage_prefix)
|
||||
self.write('\n')
|
||||
indent = ' ' * (max(self.current_indent, term_len(prefix)) + 4)
|
||||
self.write(wrap_text(args, text_width,
|
||||
initial_indent=indent,
|
||||
subsequent_indent=indent))
|
||||
|
||||
self.write('\n')
|
||||
|
||||
def write_heading(self, heading):
|
||||
"""Writes a heading into the buffer."""
|
||||
self.write('%*s%s:\n' % (self.current_indent, '', heading))
|
||||
|
||||
def write_paragraph(self):
|
||||
"""Writes a paragraph into the buffer."""
|
||||
if self.buffer:
|
||||
self.write('\n')
|
||||
|
||||
def write_text(self, text):
|
||||
"""Writes re-indented text into the buffer. This rewraps and
|
||||
preserves paragraphs.
|
||||
"""
|
||||
text_width = max(self.width - self.current_indent, 11)
|
||||
indent = ' ' * self.current_indent
|
||||
self.write(wrap_text(text, text_width,
|
||||
initial_indent=indent,
|
||||
subsequent_indent=indent,
|
||||
preserve_paragraphs=True))
|
||||
self.write('\n')
|
||||
|
||||
def write_dl(self, rows, col_max=30, col_spacing=2):
|
||||
"""Writes a definition list into the buffer. This is how options
|
||||
and commands are usually formatted.
|
||||
|
||||
:param rows: a list of two item tuples for the terms and values.
|
||||
:param col_max: the maximum width of the first column.
|
||||
:param col_spacing: the number of spaces between the first and
|
||||
second column.
|
||||
"""
|
||||
rows = list(rows)
|
||||
widths = measure_table(rows)
|
||||
if len(widths) != 2:
|
||||
raise TypeError('Expected two columns for definition list')
|
||||
|
||||
first_col = min(widths[0], col_max) + col_spacing
|
||||
|
||||
for first, second in iter_rows(rows, len(widths)):
|
||||
self.write('%*s%s' % (self.current_indent, '', first))
|
||||
if not second:
|
||||
self.write('\n')
|
||||
continue
|
||||
if term_len(first) <= first_col - col_spacing:
|
||||
self.write(' ' * (first_col - term_len(first)))
|
||||
else:
|
||||
self.write('\n')
|
||||
self.write(' ' * (first_col + self.current_indent))
|
||||
|
||||
text_width = max(self.width - first_col - 2, 10)
|
||||
lines = iter(wrap_text(second, text_width).splitlines())
|
||||
if lines:
|
||||
self.write(next(lines) + '\n')
|
||||
for line in lines:
|
||||
self.write('%*s%s\n' % (
|
||||
first_col + self.current_indent, '', line))
|
||||
else:
|
||||
self.write('\n')
|
||||
|
||||
@contextmanager
|
||||
def section(self, name):
|
||||
"""Helpful context manager that writes a paragraph, a heading,
|
||||
and the indents.
|
||||
|
||||
:param name: the section name that is written as heading.
|
||||
"""
|
||||
self.write_paragraph()
|
||||
self.write_heading(name)
|
||||
self.indent()
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
self.dedent()
|
||||
|
||||
@contextmanager
|
||||
def indentation(self):
|
||||
"""A context manager that increases the indentation."""
|
||||
self.indent()
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
self.dedent()
|
||||
|
||||
def getvalue(self):
|
||||
"""Returns the buffer contents."""
|
||||
return ''.join(self.buffer)
|
||||
|
||||
|
||||
def join_options(options):
|
||||
"""Given a list of option strings this joins them in the most appropriate
|
||||
way and returns them in the form ``(formatted_string,
|
||||
any_prefix_is_slash)`` where the second item in the tuple is a flag that
|
||||
indicates if any of the option prefixes was a slash.
|
||||
"""
|
||||
rv = []
|
||||
any_prefix_is_slash = False
|
||||
for opt in options:
|
||||
prefix = split_opt(opt)[0]
|
||||
if prefix == '/':
|
||||
any_prefix_is_slash = True
|
||||
rv.append((len(prefix), opt))
|
||||
|
||||
rv.sort(key=lambda x: x[0])
|
||||
|
||||
rv = ', '.join(x[1] for x in rv)
|
||||
return rv, any_prefix_is_slash
|
||||
@@ -1,48 +0,0 @@
|
||||
from threading import local
|
||||
|
||||
|
||||
_local = local()
|
||||
|
||||
|
||||
def get_current_context(silent=False):
|
||||
"""Returns the current click context. This can be used as a way to
|
||||
access the current context object from anywhere. This is a more implicit
|
||||
alternative to the :func:`pass_context` decorator. This function is
|
||||
primarily useful for helpers such as :func:`echo` which might be
|
||||
interested in changing it's behavior based on the current context.
|
||||
|
||||
To push the current context, :meth:`Context.scope` can be used.
|
||||
|
||||
.. versionadded:: 5.0
|
||||
|
||||
:param silent: is set to `True` the return value is `None` if no context
|
||||
is available. The default behavior is to raise a
|
||||
:exc:`RuntimeError`.
|
||||
"""
|
||||
try:
|
||||
return getattr(_local, 'stack')[-1]
|
||||
except (AttributeError, IndexError):
|
||||
if not silent:
|
||||
raise RuntimeError('There is no active click context.')
|
||||
|
||||
|
||||
def push_context(ctx):
|
||||
"""Pushes a new context to the current stack."""
|
||||
_local.__dict__.setdefault('stack', []).append(ctx)
|
||||
|
||||
|
||||
def pop_context():
|
||||
"""Removes the top level from the stack."""
|
||||
_local.stack.pop()
|
||||
|
||||
|
||||
def resolve_color_default(color=None):
|
||||
""""Internal helper to get the default value of the color flag. If a
|
||||
value is passed it's returned unchanged, otherwise it's looked up from
|
||||
the current context.
|
||||
"""
|
||||
if color is not None:
|
||||
return color
|
||||
ctx = get_current_context(silent=True)
|
||||
if ctx is not None:
|
||||
return ctx.color
|
||||
426
click/parser.py
426
click/parser.py
@@ -1,426 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
click.parser
|
||||
~~~~~~~~~~~~
|
||||
|
||||
This module started out as largely a copy paste from the stdlib's
|
||||
optparse module with the features removed that we do not need from
|
||||
optparse because we implement them in Click on a higher level (for
|
||||
instance type handling, help formatting and a lot more).
|
||||
|
||||
The plan is to remove more and more from here over time.
|
||||
|
||||
The reason this is a different module and not optparse from the stdlib
|
||||
is that there are differences in 2.x and 3.x about the error messages
|
||||
generated and optparse in the stdlib uses gettext for no good reason
|
||||
and might cause us issues.
|
||||
"""
|
||||
import re
|
||||
from collections import deque
|
||||
from .exceptions import UsageError, NoSuchOption, BadOptionUsage, \
|
||||
BadArgumentUsage
|
||||
|
||||
|
||||
def _unpack_args(args, nargs_spec):
|
||||
"""Given an iterable of arguments and an iterable of nargs specifications,
|
||||
it returns a tuple with all the unpacked arguments at the first index
|
||||
and all remaining arguments as the second.
|
||||
|
||||
The nargs specification is the number of arguments that should be consumed
|
||||
or `-1` to indicate that this position should eat up all the remainders.
|
||||
|
||||
Missing items are filled with `None`.
|
||||
"""
|
||||
args = deque(args)
|
||||
nargs_spec = deque(nargs_spec)
|
||||
rv = []
|
||||
spos = None
|
||||
|
||||
def _fetch(c):
|
||||
try:
|
||||
if spos is None:
|
||||
return c.popleft()
|
||||
else:
|
||||
return c.pop()
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
while nargs_spec:
|
||||
nargs = _fetch(nargs_spec)
|
||||
if nargs == 1:
|
||||
rv.append(_fetch(args))
|
||||
elif nargs > 1:
|
||||
x = [_fetch(args) for _ in range(nargs)]
|
||||
# If we're reversed, we're pulling in the arguments in reverse,
|
||||
# so we need to turn them around.
|
||||
if spos is not None:
|
||||
x.reverse()
|
||||
rv.append(tuple(x))
|
||||
elif nargs < 0:
|
||||
if spos is not None:
|
||||
raise TypeError('Cannot have two nargs < 0')
|
||||
spos = len(rv)
|
||||
rv.append(None)
|
||||
|
||||
# spos is the position of the wildcard (star). If it's not `None`,
|
||||
# we fill it with the remainder.
|
||||
if spos is not None:
|
||||
rv[spos] = tuple(args)
|
||||
args = []
|
||||
rv[spos + 1:] = reversed(rv[spos + 1:])
|
||||
|
||||
return tuple(rv), list(args)
|
||||
|
||||
|
||||
def _error_opt_args(nargs, opt):
|
||||
if nargs == 1:
|
||||
raise BadOptionUsage('%s option requires an argument' % opt)
|
||||
raise BadOptionUsage('%s option requires %d arguments' % (opt, nargs))
|
||||
|
||||
|
||||
def split_opt(opt):
|
||||
first = opt[:1]
|
||||
if first.isalnum():
|
||||
return '', opt
|
||||
if opt[1:2] == first:
|
||||
return opt[:2], opt[2:]
|
||||
return first, opt[1:]
|
||||
|
||||
|
||||
def normalize_opt(opt, ctx):
|
||||
if ctx is None or ctx.token_normalize_func is None:
|
||||
return opt
|
||||
prefix, opt = split_opt(opt)
|
||||
return prefix + ctx.token_normalize_func(opt)
|
||||
|
||||
|
||||
def split_arg_string(string):
|
||||
"""Given an argument string this attempts to split it into small parts."""
|
||||
rv = []
|
||||
for match in re.finditer(r"('([^'\\]*(?:\\.[^'\\]*)*)'"
|
||||
r'|"([^"\\]*(?:\\.[^"\\]*)*)"'
|
||||
r'|\S+)\s*', string, re.S):
|
||||
arg = match.group().strip()
|
||||
if arg[:1] == arg[-1:] and arg[:1] in '"\'':
|
||||
arg = arg[1:-1].encode('ascii', 'backslashreplace') \
|
||||
.decode('unicode-escape')
|
||||
try:
|
||||
arg = type(string)(arg)
|
||||
except UnicodeError:
|
||||
pass
|
||||
rv.append(arg)
|
||||
return rv
|
||||
|
||||
|
||||
class Option(object):
|
||||
|
||||
def __init__(self, opts, dest, action=None, nargs=1, const=None, obj=None):
|
||||
self._short_opts = []
|
||||
self._long_opts = []
|
||||
self.prefixes = set()
|
||||
|
||||
for opt in opts:
|
||||
prefix, value = split_opt(opt)
|
||||
if not prefix:
|
||||
raise ValueError('Invalid start character for option (%s)'
|
||||
% opt)
|
||||
self.prefixes.add(prefix[0])
|
||||
if len(prefix) == 1 and len(value) == 1:
|
||||
self._short_opts.append(opt)
|
||||
else:
|
||||
self._long_opts.append(opt)
|
||||
self.prefixes.add(prefix)
|
||||
|
||||
if action is None:
|
||||
action = 'store'
|
||||
|
||||
self.dest = dest
|
||||
self.action = action
|
||||
self.nargs = nargs
|
||||
self.const = const
|
||||
self.obj = obj
|
||||
|
||||
@property
|
||||
def takes_value(self):
|
||||
return self.action in ('store', 'append')
|
||||
|
||||
def process(self, value, state):
|
||||
if self.action == 'store':
|
||||
state.opts[self.dest] = value
|
||||
elif self.action == 'store_const':
|
||||
state.opts[self.dest] = self.const
|
||||
elif self.action == 'append':
|
||||
state.opts.setdefault(self.dest, []).append(value)
|
||||
elif self.action == 'append_const':
|
||||
state.opts.setdefault(self.dest, []).append(self.const)
|
||||
elif self.action == 'count':
|
||||
state.opts[self.dest] = state.opts.get(self.dest, 0) + 1
|
||||
else:
|
||||
raise ValueError('unknown action %r' % self.action)
|
||||
state.order.append(self.obj)
|
||||
|
||||
|
||||
class Argument(object):
|
||||
|
||||
def __init__(self, dest, nargs=1, obj=None):
|
||||
self.dest = dest
|
||||
self.nargs = nargs
|
||||
self.obj = obj
|
||||
|
||||
def process(self, value, state):
|
||||
if self.nargs > 1:
|
||||
holes = sum(1 for x in value if x is None)
|
||||
if holes == len(value):
|
||||
value = None
|
||||
elif holes != 0:
|
||||
raise BadArgumentUsage('argument %s takes %d values'
|
||||
% (self.dest, self.nargs))
|
||||
state.opts[self.dest] = value
|
||||
state.order.append(self.obj)
|
||||
|
||||
|
||||
class ParsingState(object):
|
||||
|
||||
def __init__(self, rargs):
|
||||
self.opts = {}
|
||||
self.largs = []
|
||||
self.rargs = rargs
|
||||
self.order = []
|
||||
|
||||
|
||||
class OptionParser(object):
|
||||
"""The option parser is an internal class that is ultimately used to
|
||||
parse options and arguments. It's modelled after optparse and brings
|
||||
a similar but vastly simplified API. It should generally not be used
|
||||
directly as the high level Click classes wrap it for you.
|
||||
|
||||
It's not nearly as extensible as optparse or argparse as it does not
|
||||
implement features that are implemented on a higher level (such as
|
||||
types or defaults).
|
||||
|
||||
:param ctx: optionally the :class:`~click.Context` where this parser
|
||||
should go with.
|
||||
"""
|
||||
|
||||
def __init__(self, ctx=None):
|
||||
#: The :class:`~click.Context` for this parser. This might be
|
||||
#: `None` for some advanced use cases.
|
||||
self.ctx = ctx
|
||||
#: This controls how the parser deals with interspersed arguments.
|
||||
#: If this is set to `False`, the parser will stop on the first
|
||||
#: non-option. Click uses this to implement nested subcommands
|
||||
#: safely.
|
||||
self.allow_interspersed_args = True
|
||||
#: This tells the parser how to deal with unknown options. By
|
||||
#: default it will error out (which is sensible), but there is a
|
||||
#: second mode where it will ignore it and continue processing
|
||||
#: after shifting all the unknown options into the resulting args.
|
||||
self.ignore_unknown_options = False
|
||||
if ctx is not None:
|
||||
self.allow_interspersed_args = ctx.allow_interspersed_args
|
||||
self.ignore_unknown_options = ctx.ignore_unknown_options
|
||||
self._short_opt = {}
|
||||
self._long_opt = {}
|
||||
self._opt_prefixes = set(['-', '--'])
|
||||
self._args = []
|
||||
|
||||
def add_option(self, opts, dest, action=None, nargs=1, const=None,
|
||||
obj=None):
|
||||
"""Adds a new option named `dest` to the parser. The destination
|
||||
is not inferred (unlike with optparse) and needs to be explicitly
|
||||
provided. Action can be any of ``store``, ``store_const``,
|
||||
``append``, ``appnd_const`` or ``count``.
|
||||
|
||||
The `obj` can be used to identify the option in the order list
|
||||
that is returned from the parser.
|
||||
"""
|
||||
if obj is None:
|
||||
obj = dest
|
||||
opts = [normalize_opt(opt, self.ctx) for opt in opts]
|
||||
option = Option(opts, dest, action=action, nargs=nargs,
|
||||
const=const, obj=obj)
|
||||
self._opt_prefixes.update(option.prefixes)
|
||||
for opt in option._short_opts:
|
||||
self._short_opt[opt] = option
|
||||
for opt in option._long_opts:
|
||||
self._long_opt[opt] = option
|
||||
|
||||
def add_argument(self, dest, nargs=1, obj=None):
|
||||
"""Adds a positional argument named `dest` to the parser.
|
||||
|
||||
The `obj` can be used to identify the option in the order list
|
||||
that is returned from the parser.
|
||||
"""
|
||||
if obj is None:
|
||||
obj = dest
|
||||
self._args.append(Argument(dest=dest, nargs=nargs, obj=obj))
|
||||
|
||||
def parse_args(self, args):
|
||||
"""Parses positional arguments and returns ``(values, args, order)``
|
||||
for the parsed options and arguments as well as the leftover
|
||||
arguments if there are any. The order is a list of objects as they
|
||||
appear on the command line. If arguments appear multiple times they
|
||||
will be memorized multiple times as well.
|
||||
"""
|
||||
state = ParsingState(args)
|
||||
try:
|
||||
self._process_args_for_options(state)
|
||||
self._process_args_for_args(state)
|
||||
except UsageError:
|
||||
if self.ctx is None or not self.ctx.resilient_parsing:
|
||||
raise
|
||||
return state.opts, state.largs, state.order
|
||||
|
||||
def _process_args_for_args(self, state):
|
||||
pargs, args = _unpack_args(state.largs + state.rargs,
|
||||
[x.nargs for x in self._args])
|
||||
|
||||
for idx, arg in enumerate(self._args):
|
||||
arg.process(pargs[idx], state)
|
||||
|
||||
state.largs = args
|
||||
state.rargs = []
|
||||
|
||||
def _process_args_for_options(self, state):
|
||||
while state.rargs:
|
||||
arg = state.rargs.pop(0)
|
||||
arglen = len(arg)
|
||||
# Double dashes always handled explicitly regardless of what
|
||||
# prefixes are valid.
|
||||
if arg == '--':
|
||||
return
|
||||
elif arg[:1] in self._opt_prefixes and arglen > 1:
|
||||
self._process_opts(arg, state)
|
||||
elif self.allow_interspersed_args:
|
||||
state.largs.append(arg)
|
||||
else:
|
||||
state.rargs.insert(0, arg)
|
||||
return
|
||||
|
||||
# Say this is the original argument list:
|
||||
# [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)]
|
||||
# ^
|
||||
# (we are about to process arg(i)).
|
||||
#
|
||||
# Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of
|
||||
# [arg0, ..., arg(i-1)] (any options and their arguments will have
|
||||
# been removed from largs).
|
||||
#
|
||||
# The while loop will usually consume 1 or more arguments per pass.
|
||||
# If it consumes 1 (eg. arg is an option that takes no arguments),
|
||||
# then after _process_arg() is done the situation is:
|
||||
#
|
||||
# largs = subset of [arg0, ..., arg(i)]
|
||||
# rargs = [arg(i+1), ..., arg(N-1)]
|
||||
#
|
||||
# If allow_interspersed_args is false, largs will always be
|
||||
# *empty* -- still a subset of [arg0, ..., arg(i-1)], but
|
||||
# not a very interesting subset!
|
||||
|
||||
def _match_long_opt(self, opt, explicit_value, state):
|
||||
if opt not in self._long_opt:
|
||||
possibilities = [word for word in self._long_opt
|
||||
if word.startswith(opt)]
|
||||
raise NoSuchOption(opt, possibilities=possibilities)
|
||||
|
||||
option = self._long_opt[opt]
|
||||
if option.takes_value:
|
||||
# At this point it's safe to modify rargs by injecting the
|
||||
# explicit value, because no exception is raised in this
|
||||
# branch. This means that the inserted value will be fully
|
||||
# consumed.
|
||||
if explicit_value is not None:
|
||||
state.rargs.insert(0, explicit_value)
|
||||
|
||||
nargs = option.nargs
|
||||
if len(state.rargs) < nargs:
|
||||
_error_opt_args(nargs, opt)
|
||||
elif nargs == 1:
|
||||
value = state.rargs.pop(0)
|
||||
else:
|
||||
value = tuple(state.rargs[:nargs])
|
||||
del state.rargs[:nargs]
|
||||
|
||||
elif explicit_value is not None:
|
||||
raise BadOptionUsage('%s option does not take a value' % opt)
|
||||
|
||||
else:
|
||||
value = None
|
||||
|
||||
option.process(value, state)
|
||||
|
||||
def _match_short_opt(self, arg, state):
|
||||
stop = False
|
||||
i = 1
|
||||
prefix = arg[0]
|
||||
unknown_options = []
|
||||
|
||||
for ch in arg[1:]:
|
||||
opt = normalize_opt(prefix + ch, self.ctx)
|
||||
option = self._short_opt.get(opt)
|
||||
i += 1
|
||||
|
||||
if not option:
|
||||
if self.ignore_unknown_options:
|
||||
unknown_options.append(ch)
|
||||
continue
|
||||
raise NoSuchOption(opt)
|
||||
if option.takes_value:
|
||||
# Any characters left in arg? Pretend they're the
|
||||
# next arg, and stop consuming characters of arg.
|
||||
if i < len(arg):
|
||||
state.rargs.insert(0, arg[i:])
|
||||
stop = True
|
||||
|
||||
nargs = option.nargs
|
||||
if len(state.rargs) < nargs:
|
||||
_error_opt_args(nargs, opt)
|
||||
elif nargs == 1:
|
||||
value = state.rargs.pop(0)
|
||||
else:
|
||||
value = tuple(state.rargs[:nargs])
|
||||
del state.rargs[:nargs]
|
||||
|
||||
else:
|
||||
value = None
|
||||
|
||||
option.process(value, state)
|
||||
|
||||
if stop:
|
||||
break
|
||||
|
||||
# If we got any unknown options we re-combinate the string of the
|
||||
# remaining options and re-attach the prefix, then report that
|
||||
# to the state as new larg. This way there is basic combinatorics
|
||||
# that can be achieved while still ignoring unknown arguments.
|
||||
if self.ignore_unknown_options and unknown_options:
|
||||
state.largs.append(prefix + ''.join(unknown_options))
|
||||
|
||||
def _process_opts(self, arg, state):
|
||||
explicit_value = None
|
||||
# Long option handling happens in two parts. The first part is
|
||||
# supporting explicitly attached values. In any case, we will try
|
||||
# to long match the option first.
|
||||
if '=' in arg:
|
||||
long_opt, explicit_value = arg.split('=', 1)
|
||||
else:
|
||||
long_opt = arg
|
||||
norm_long_opt = normalize_opt(long_opt, self.ctx)
|
||||
|
||||
# At this point we will match the (assumed) long option through
|
||||
# the long option matching code. Note that this allows options
|
||||
# like "-foo" to be matched as long options.
|
||||
try:
|
||||
self._match_long_opt(norm_long_opt, explicit_value, state)
|
||||
except NoSuchOption:
|
||||
# At this point the long option matching failed, and we need
|
||||
# to try with short options. However there is a special rule
|
||||
# which says, that if we have a two character options prefix
|
||||
# (applies to "--foo" for instance), we do not dispatch to the
|
||||
# short option code and will instead raise the no option
|
||||
# error.
|
||||
if arg[:2] not in self._opt_prefixes:
|
||||
return self._match_short_opt(arg, state)
|
||||
if not self.ignore_unknown_options:
|
||||
raise
|
||||
state.largs.append(arg)
|
||||
539
click/termui.py
539
click/termui.py
@@ -1,539 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
import struct
|
||||
|
||||
from ._compat import raw_input, text_type, string_types, \
|
||||
isatty, strip_ansi, get_winterm_size, DEFAULT_COLUMNS, WIN
|
||||
from .utils import echo
|
||||
from .exceptions import Abort, UsageError
|
||||
from .types import convert_type
|
||||
from .globals import resolve_color_default
|
||||
|
||||
|
||||
# The prompt functions to use. The doc tools currently override these
|
||||
# functions to customize how they work.
|
||||
visible_prompt_func = raw_input
|
||||
|
||||
_ansi_colors = ('black', 'red', 'green', 'yellow', 'blue', 'magenta',
|
||||
'cyan', 'white', 'reset')
|
||||
_ansi_reset_all = '\033[0m'
|
||||
|
||||
|
||||
def hidden_prompt_func(prompt):
|
||||
import getpass
|
||||
return getpass.getpass(prompt)
|
||||
|
||||
|
||||
def _build_prompt(text, suffix, show_default=False, default=None):
|
||||
prompt = text
|
||||
if default is not None and show_default:
|
||||
prompt = '%s [%s]' % (prompt, default)
|
||||
return prompt + suffix
|
||||
|
||||
|
||||
def prompt(text, default=None, hide_input=False,
|
||||
confirmation_prompt=False, type=None,
|
||||
value_proc=None, prompt_suffix=': ',
|
||||
show_default=True, err=False):
|
||||
"""Prompts a user for input. This is a convenience function that can
|
||||
be used to prompt a user for input later.
|
||||
|
||||
If the user aborts the input by sending a interrupt signal, this
|
||||
function will catch it and raise a :exc:`Abort` exception.
|
||||
|
||||
.. versionadded:: 6.0
|
||||
Added unicode support for cmd.exe on Windows.
|
||||
|
||||
.. versionadded:: 4.0
|
||||
Added the `err` parameter.
|
||||
|
||||
:param text: the text to show for the prompt.
|
||||
:param default: the default value to use if no input happens. If this
|
||||
is not given it will prompt until it's aborted.
|
||||
:param hide_input: if this is set to true then the input value will
|
||||
be hidden.
|
||||
:param confirmation_prompt: asks for confirmation for the value.
|
||||
:param type: the type to use to check the value against.
|
||||
:param value_proc: if this parameter is provided it's a function that
|
||||
is invoked instead of the type conversion to
|
||||
convert a value.
|
||||
:param prompt_suffix: a suffix that should be added to the prompt.
|
||||
:param show_default: shows or hides the default value in the prompt.
|
||||
:param err: if set to true the file defaults to ``stderr`` instead of
|
||||
``stdout``, the same as with echo.
|
||||
"""
|
||||
result = None
|
||||
|
||||
def prompt_func(text):
|
||||
f = hide_input and hidden_prompt_func or visible_prompt_func
|
||||
try:
|
||||
# Write the prompt separately so that we get nice
|
||||
# coloring through colorama on Windows
|
||||
echo(text, nl=False, err=err)
|
||||
return f('')
|
||||
except (KeyboardInterrupt, EOFError):
|
||||
# getpass doesn't print a newline if the user aborts input with ^C.
|
||||
# Allegedly this behavior is inherited from getpass(3).
|
||||
# A doc bug has been filed at https://bugs.python.org/issue24711
|
||||
if hide_input:
|
||||
echo(None, err=err)
|
||||
raise Abort()
|
||||
|
||||
if value_proc is None:
|
||||
value_proc = convert_type(type, default)
|
||||
|
||||
prompt = _build_prompt(text, prompt_suffix, show_default, default)
|
||||
|
||||
while 1:
|
||||
while 1:
|
||||
value = prompt_func(prompt)
|
||||
if value:
|
||||
break
|
||||
# If a default is set and used, then the confirmation
|
||||
# prompt is always skipped because that's the only thing
|
||||
# that really makes sense.
|
||||
elif default is not None:
|
||||
return default
|
||||
try:
|
||||
result = value_proc(value)
|
||||
except UsageError as e:
|
||||
echo('Error: %s' % e.message, err=err)
|
||||
continue
|
||||
if not confirmation_prompt:
|
||||
return result
|
||||
while 1:
|
||||
value2 = prompt_func('Repeat for confirmation: ')
|
||||
if value2:
|
||||
break
|
||||
if value == value2:
|
||||
return result
|
||||
echo('Error: the two entered values do not match', err=err)
|
||||
|
||||
|
||||
def confirm(text, default=False, abort=False, prompt_suffix=': ',
|
||||
show_default=True, err=False):
|
||||
"""Prompts for confirmation (yes/no question).
|
||||
|
||||
If the user aborts the input by sending a interrupt signal this
|
||||
function will catch it and raise a :exc:`Abort` exception.
|
||||
|
||||
.. versionadded:: 4.0
|
||||
Added the `err` parameter.
|
||||
|
||||
:param text: the question to ask.
|
||||
:param default: the default for the prompt.
|
||||
:param abort: if this is set to `True` a negative answer aborts the
|
||||
exception by raising :exc:`Abort`.
|
||||
:param prompt_suffix: a suffix that should be added to the prompt.
|
||||
:param show_default: shows or hides the default value in the prompt.
|
||||
:param err: if set to true the file defaults to ``stderr`` instead of
|
||||
``stdout``, the same as with echo.
|
||||
"""
|
||||
prompt = _build_prompt(text, prompt_suffix, show_default,
|
||||
default and 'Y/n' or 'y/N')
|
||||
while 1:
|
||||
try:
|
||||
# Write the prompt separately so that we get nice
|
||||
# coloring through colorama on Windows
|
||||
echo(prompt, nl=False, err=err)
|
||||
value = visible_prompt_func('').lower().strip()
|
||||
except (KeyboardInterrupt, EOFError):
|
||||
raise Abort()
|
||||
if value in ('y', 'yes'):
|
||||
rv = True
|
||||
elif value in ('n', 'no'):
|
||||
rv = False
|
||||
elif value == '':
|
||||
rv = default
|
||||
else:
|
||||
echo('Error: invalid input', err=err)
|
||||
continue
|
||||
break
|
||||
if abort and not rv:
|
||||
raise Abort()
|
||||
return rv
|
||||
|
||||
|
||||
def get_terminal_size():
|
||||
"""Returns the current size of the terminal as tuple in the form
|
||||
``(width, height)`` in columns and rows.
|
||||
"""
|
||||
# If shutil has get_terminal_size() (Python 3.3 and later) use that
|
||||
if sys.version_info >= (3, 3):
|
||||
import shutil
|
||||
shutil_get_terminal_size = getattr(shutil, 'get_terminal_size', None)
|
||||
if shutil_get_terminal_size:
|
||||
sz = shutil_get_terminal_size()
|
||||
return sz.columns, sz.lines
|
||||
|
||||
if get_winterm_size is not None:
|
||||
return get_winterm_size()
|
||||
|
||||
def ioctl_gwinsz(fd):
|
||||
try:
|
||||
import fcntl
|
||||
import termios
|
||||
cr = struct.unpack(
|
||||
'hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
|
||||
except Exception:
|
||||
return
|
||||
return cr
|
||||
|
||||
cr = ioctl_gwinsz(0) or ioctl_gwinsz(1) or ioctl_gwinsz(2)
|
||||
if not cr:
|
||||
try:
|
||||
fd = os.open(os.ctermid(), os.O_RDONLY)
|
||||
try:
|
||||
cr = ioctl_gwinsz(fd)
|
||||
finally:
|
||||
os.close(fd)
|
||||
except Exception:
|
||||
pass
|
||||
if not cr or not cr[0] or not cr[1]:
|
||||
cr = (os.environ.get('LINES', 25),
|
||||
os.environ.get('COLUMNS', DEFAULT_COLUMNS))
|
||||
return int(cr[1]), int(cr[0])
|
||||
|
||||
|
||||
def echo_via_pager(text, color=None):
|
||||
"""This function takes a text and shows it via an environment specific
|
||||
pager on stdout.
|
||||
|
||||
.. versionchanged:: 3.0
|
||||
Added the `color` flag.
|
||||
|
||||
:param text: the text to page.
|
||||
:param color: controls if the pager supports ANSI colors or not. The
|
||||
default is autodetection.
|
||||
"""
|
||||
color = resolve_color_default(color)
|
||||
if not isinstance(text, string_types):
|
||||
text = text_type(text)
|
||||
from ._termui_impl import pager
|
||||
return pager(text + '\n', color)
|
||||
|
||||
|
||||
def progressbar(iterable=None, length=None, label=None, show_eta=True,
|
||||
show_percent=None, show_pos=False,
|
||||
item_show_func=None, fill_char='#', empty_char='-',
|
||||
bar_template='%(label)s [%(bar)s] %(info)s',
|
||||
info_sep=' ', width=36, file=None, color=None):
|
||||
"""This function creates an iterable context manager that can be used
|
||||
to iterate over something while showing a progress bar. It will
|
||||
either iterate over the `iterable` or `length` items (that are counted
|
||||
up). While iteration happens, this function will print a rendered
|
||||
progress bar to the given `file` (defaults to stdout) and will attempt
|
||||
to calculate remaining time and more. By default, this progress bar
|
||||
will not be rendered if the file is not a terminal.
|
||||
|
||||
The context manager creates the progress bar. When the context
|
||||
manager is entered the progress bar is already displayed. With every
|
||||
iteration over the progress bar, the iterable passed to the bar is
|
||||
advanced and the bar is updated. When the context manager exits,
|
||||
a newline is printed and the progress bar is finalized on screen.
|
||||
|
||||
No printing must happen or the progress bar will be unintentionally
|
||||
destroyed.
|
||||
|
||||
Example usage::
|
||||
|
||||
with progressbar(items) as bar:
|
||||
for item in bar:
|
||||
do_something_with(item)
|
||||
|
||||
Alternatively, if no iterable is specified, one can manually update the
|
||||
progress bar through the `update()` method instead of directly
|
||||
iterating over the progress bar. The update method accepts the number
|
||||
of steps to increment the bar with::
|
||||
|
||||
with progressbar(length=chunks.total_bytes) as bar:
|
||||
for chunk in chunks:
|
||||
process_chunk(chunk)
|
||||
bar.update(chunks.bytes)
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
.. versionadded:: 4.0
|
||||
Added the `color` parameter. Added a `update` method to the
|
||||
progressbar object.
|
||||
|
||||
:param iterable: an iterable to iterate over. If not provided the length
|
||||
is required.
|
||||
:param length: the number of items to iterate over. By default the
|
||||
progressbar will attempt to ask the iterator about its
|
||||
length, which might or might not work. If an iterable is
|
||||
also provided this parameter can be used to override the
|
||||
length. If an iterable is not provided the progress bar
|
||||
will iterate over a range of that length.
|
||||
:param label: the label to show next to the progress bar.
|
||||
:param show_eta: enables or disables the estimated time display. This is
|
||||
automatically disabled if the length cannot be
|
||||
determined.
|
||||
:param show_percent: enables or disables the percentage display. The
|
||||
default is `True` if the iterable has a length or
|
||||
`False` if not.
|
||||
:param show_pos: enables or disables the absolute position display. The
|
||||
default is `False`.
|
||||
:param item_show_func: a function called with the current item which
|
||||
can return a string to show the current item
|
||||
next to the progress bar. Note that the current
|
||||
item can be `None`!
|
||||
:param fill_char: the character to use to show the filled part of the
|
||||
progress bar.
|
||||
:param empty_char: the character to use to show the non-filled part of
|
||||
the progress bar.
|
||||
:param bar_template: the format string to use as template for the bar.
|
||||
The parameters in it are ``label`` for the label,
|
||||
``bar`` for the progress bar and ``info`` for the
|
||||
info section.
|
||||
:param info_sep: the separator between multiple info items (eta etc.)
|
||||
:param width: the width of the progress bar in characters, 0 means full
|
||||
terminal width
|
||||
:param file: the file to write to. If this is not a terminal then
|
||||
only the label is printed.
|
||||
:param color: controls if the terminal supports ANSI colors or not. The
|
||||
default is autodetection. This is only needed if ANSI
|
||||
codes are included anywhere in the progress bar output
|
||||
which is not the case by default.
|
||||
"""
|
||||
from ._termui_impl import ProgressBar
|
||||
color = resolve_color_default(color)
|
||||
return ProgressBar(iterable=iterable, length=length, show_eta=show_eta,
|
||||
show_percent=show_percent, show_pos=show_pos,
|
||||
item_show_func=item_show_func, fill_char=fill_char,
|
||||
empty_char=empty_char, bar_template=bar_template,
|
||||
info_sep=info_sep, file=file, label=label,
|
||||
width=width, color=color)
|
||||
|
||||
|
||||
def clear():
|
||||
"""Clears the terminal screen. This will have the effect of clearing
|
||||
the whole visible space of the terminal and moving the cursor to the
|
||||
top left. This does not do anything if not connected to a terminal.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
if not isatty(sys.stdout):
|
||||
return
|
||||
# If we're on Windows and we don't have colorama available, then we
|
||||
# clear the screen by shelling out. Otherwise we can use an escape
|
||||
# sequence.
|
||||
if WIN:
|
||||
os.system('cls')
|
||||
else:
|
||||
sys.stdout.write('\033[2J\033[1;1H')
|
||||
|
||||
|
||||
def style(text, fg=None, bg=None, bold=None, dim=None, underline=None,
|
||||
blink=None, reverse=None, reset=True):
|
||||
"""Styles a text with ANSI styles and returns the new string. By
|
||||
default the styling is self contained which means that at the end
|
||||
of the string a reset code is issued. This can be prevented by
|
||||
passing ``reset=False``.
|
||||
|
||||
Examples::
|
||||
|
||||
click.echo(click.style('Hello World!', fg='green'))
|
||||
click.echo(click.style('ATTENTION!', blink=True))
|
||||
click.echo(click.style('Some things', reverse=True, fg='cyan'))
|
||||
|
||||
Supported color names:
|
||||
|
||||
* ``black`` (might be a gray)
|
||||
* ``red``
|
||||
* ``green``
|
||||
* ``yellow`` (might be an orange)
|
||||
* ``blue``
|
||||
* ``magenta``
|
||||
* ``cyan``
|
||||
* ``white`` (might be light gray)
|
||||
* ``reset`` (reset the color code only)
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
:param text: the string to style with ansi codes.
|
||||
:param fg: if provided this will become the foreground color.
|
||||
:param bg: if provided this will become the background color.
|
||||
:param bold: if provided this will enable or disable bold mode.
|
||||
:param dim: if provided this will enable or disable dim mode. This is
|
||||
badly supported.
|
||||
:param underline: if provided this will enable or disable underline.
|
||||
:param blink: if provided this will enable or disable blinking.
|
||||
:param reverse: if provided this will enable or disable inverse
|
||||
rendering (foreground becomes background and the
|
||||
other way round).
|
||||
:param reset: by default a reset-all code is added at the end of the
|
||||
string which means that styles do not carry over. This
|
||||
can be disabled to compose styles.
|
||||
"""
|
||||
bits = []
|
||||
if fg:
|
||||
try:
|
||||
bits.append('\033[%dm' % (_ansi_colors.index(fg) + 30))
|
||||
except ValueError:
|
||||
raise TypeError('Unknown color %r' % fg)
|
||||
if bg:
|
||||
try:
|
||||
bits.append('\033[%dm' % (_ansi_colors.index(bg) + 40))
|
||||
except ValueError:
|
||||
raise TypeError('Unknown color %r' % bg)
|
||||
if bold is not None:
|
||||
bits.append('\033[%dm' % (1 if bold else 22))
|
||||
if dim is not None:
|
||||
bits.append('\033[%dm' % (2 if dim else 22))
|
||||
if underline is not None:
|
||||
bits.append('\033[%dm' % (4 if underline else 24))
|
||||
if blink is not None:
|
||||
bits.append('\033[%dm' % (5 if blink else 25))
|
||||
if reverse is not None:
|
||||
bits.append('\033[%dm' % (7 if reverse else 27))
|
||||
bits.append(text)
|
||||
if reset:
|
||||
bits.append(_ansi_reset_all)
|
||||
return ''.join(bits)
|
||||
|
||||
|
||||
def unstyle(text):
|
||||
"""Removes ANSI styling information from a string. Usually it's not
|
||||
necessary to use this function as Click's echo function will
|
||||
automatically remove styling if necessary.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
:param text: the text to remove style information from.
|
||||
"""
|
||||
return strip_ansi(text)
|
||||
|
||||
|
||||
def secho(text, file=None, nl=True, err=False, color=None, **styles):
|
||||
"""This function combines :func:`echo` and :func:`style` into one
|
||||
call. As such the following two calls are the same::
|
||||
|
||||
click.secho('Hello World!', fg='green')
|
||||
click.echo(click.style('Hello World!', fg='green'))
|
||||
|
||||
All keyword arguments are forwarded to the underlying functions
|
||||
depending on which one they go with.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
return echo(style(text, **styles), file=file, nl=nl, err=err, color=color)
|
||||
|
||||
|
||||
def edit(text=None, editor=None, env=None, require_save=True,
|
||||
extension='.txt', filename=None):
|
||||
r"""Edits the given text in the defined editor. If an editor is given
|
||||
(should be the full path to the executable but the regular operating
|
||||
system search path is used for finding the executable) it overrides
|
||||
the detected editor. Optionally, some environment variables can be
|
||||
used. If the editor is closed without changes, `None` is returned. In
|
||||
case a file is edited directly the return value is always `None` and
|
||||
`require_save` and `extension` are ignored.
|
||||
|
||||
If the editor cannot be opened a :exc:`UsageError` is raised.
|
||||
|
||||
Note for Windows: to simplify cross-platform usage, the newlines are
|
||||
automatically converted from POSIX to Windows and vice versa. As such,
|
||||
the message here will have ``\n`` as newline markers.
|
||||
|
||||
:param text: the text to edit.
|
||||
:param editor: optionally the editor to use. Defaults to automatic
|
||||
detection.
|
||||
:param env: environment variables to forward to the editor.
|
||||
:param require_save: if this is true, then not saving in the editor
|
||||
will make the return value become `None`.
|
||||
:param extension: the extension to tell the editor about. This defaults
|
||||
to `.txt` but changing this might change syntax
|
||||
highlighting.
|
||||
:param filename: if provided it will edit this file instead of the
|
||||
provided text contents. It will not use a temporary
|
||||
file as an indirection in that case.
|
||||
"""
|
||||
from ._termui_impl import Editor
|
||||
editor = Editor(editor=editor, env=env, require_save=require_save,
|
||||
extension=extension)
|
||||
if filename is None:
|
||||
return editor.edit(text)
|
||||
editor.edit_file(filename)
|
||||
|
||||
|
||||
def launch(url, wait=False, locate=False):
|
||||
"""This function launches the given URL (or filename) in the default
|
||||
viewer application for this file type. If this is an executable, it
|
||||
might launch the executable in a new session. The return value is
|
||||
the exit code of the launched application. Usually, ``0`` indicates
|
||||
success.
|
||||
|
||||
Examples::
|
||||
|
||||
click.launch('http://click.pocoo.org/')
|
||||
click.launch('/my/downloaded/file', locate=True)
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
:param url: URL or filename of the thing to launch.
|
||||
:param wait: waits for the program to stop.
|
||||
:param locate: if this is set to `True` then instead of launching the
|
||||
application associated with the URL it will attempt to
|
||||
launch a file manager with the file located. This
|
||||
might have weird effects if the URL does not point to
|
||||
the filesystem.
|
||||
"""
|
||||
from ._termui_impl import open_url
|
||||
return open_url(url, wait=wait, locate=locate)
|
||||
|
||||
|
||||
# If this is provided, getchar() calls into this instead. This is used
|
||||
# for unittesting purposes.
|
||||
_getchar = None
|
||||
|
||||
|
||||
def getchar(echo=False):
|
||||
"""Fetches a single character from the terminal and returns it. This
|
||||
will always return a unicode character and under certain rare
|
||||
circumstances this might return more than one character. The
|
||||
situations which more than one character is returned is when for
|
||||
whatever reason multiple characters end up in the terminal buffer or
|
||||
standard input was not actually a terminal.
|
||||
|
||||
Note that this will always read from the terminal, even if something
|
||||
is piped into the standard input.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
:param echo: if set to `True`, the character read will also show up on
|
||||
the terminal. The default is to not show it.
|
||||
"""
|
||||
f = _getchar
|
||||
if f is None:
|
||||
from ._termui_impl import getchar as f
|
||||
return f(echo)
|
||||
|
||||
|
||||
def pause(info='Press any key to continue ...', err=False):
|
||||
"""This command stops execution and waits for the user to press any
|
||||
key to continue. This is similar to the Windows batch "pause"
|
||||
command. If the program is not run through a terminal, this command
|
||||
will instead do nothing.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
.. versionadded:: 4.0
|
||||
Added the `err` parameter.
|
||||
|
||||
:param info: the info string to print before pausing.
|
||||
:param err: if set to message goes to ``stderr`` instead of
|
||||
``stdout``, the same as with echo.
|
||||
"""
|
||||
if not isatty(sys.stdin) or not isatty(sys.stdout):
|
||||
return
|
||||
try:
|
||||
if info:
|
||||
echo(info, nl=False, err=err)
|
||||
try:
|
||||
getchar()
|
||||
except (KeyboardInterrupt, EOFError):
|
||||
pass
|
||||
finally:
|
||||
if info:
|
||||
echo(err=err)
|
||||
322
click/testing.py
322
click/testing.py
@@ -1,322 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import tempfile
|
||||
import contextlib
|
||||
|
||||
from ._compat import iteritems, PY2
|
||||
|
||||
|
||||
# If someone wants to vendor click, we want to ensure the
|
||||
# correct package is discovered. Ideally we could use a
|
||||
# relative import here but unfortunately Python does not
|
||||
# support that.
|
||||
clickpkg = sys.modules[__name__.rsplit('.', 1)[0]]
|
||||
|
||||
|
||||
if PY2:
|
||||
from cStringIO import StringIO
|
||||
else:
|
||||
import io
|
||||
from ._compat import _find_binary_reader
|
||||
|
||||
|
||||
class EchoingStdin(object):
|
||||
|
||||
def __init__(self, input, output):
|
||||
self._input = input
|
||||
self._output = output
|
||||
|
||||
def __getattr__(self, x):
|
||||
return getattr(self._input, x)
|
||||
|
||||
def _echo(self, rv):
|
||||
self._output.write(rv)
|
||||
return rv
|
||||
|
||||
def read(self, n=-1):
|
||||
return self._echo(self._input.read(n))
|
||||
|
||||
def readline(self, n=-1):
|
||||
return self._echo(self._input.readline(n))
|
||||
|
||||
def readlines(self):
|
||||
return [self._echo(x) for x in self._input.readlines()]
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._echo(x) for x in self._input)
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self._input)
|
||||
|
||||
|
||||
def make_input_stream(input, charset):
|
||||
# Is already an input stream.
|
||||
if hasattr(input, 'read'):
|
||||
if PY2:
|
||||
return input
|
||||
rv = _find_binary_reader(input)
|
||||
if rv is not None:
|
||||
return rv
|
||||
raise TypeError('Could not find binary reader for input stream.')
|
||||
|
||||
if input is None:
|
||||
input = b''
|
||||
elif not isinstance(input, bytes):
|
||||
input = input.encode(charset)
|
||||
if PY2:
|
||||
return StringIO(input)
|
||||
return io.BytesIO(input)
|
||||
|
||||
|
||||
class Result(object):
|
||||
"""Holds the captured result of an invoked CLI script."""
|
||||
|
||||
def __init__(self, runner, output_bytes, exit_code, exception,
|
||||
exc_info=None):
|
||||
#: The runner that created the result
|
||||
self.runner = runner
|
||||
#: The output as bytes.
|
||||
self.output_bytes = output_bytes
|
||||
#: The exit code as integer.
|
||||
self.exit_code = exit_code
|
||||
#: The exception that happend if one did.
|
||||
self.exception = exception
|
||||
#: The traceback
|
||||
self.exc_info = exc_info
|
||||
|
||||
@property
|
||||
def output(self):
|
||||
"""The output as unicode string."""
|
||||
return self.output_bytes.decode(self.runner.charset, 'replace') \
|
||||
.replace('\r\n', '\n')
|
||||
|
||||
def __repr__(self):
|
||||
return '<Result %s>' % (
|
||||
self.exception and repr(self.exception) or 'okay',
|
||||
)
|
||||
|
||||
|
||||
class CliRunner(object):
|
||||
"""The CLI runner provides functionality to invoke a Click command line
|
||||
script for unittesting purposes in a isolated environment. This only
|
||||
works in single-threaded systems without any concurrency as it changes the
|
||||
global interpreter state.
|
||||
|
||||
:param charset: the character set for the input and output data. This is
|
||||
UTF-8 by default and should not be changed currently as
|
||||
the reporting to Click only works in Python 2 properly.
|
||||
:param env: a dictionary with environment variables for overriding.
|
||||
:param echo_stdin: if this is set to `True`, then reading from stdin writes
|
||||
to stdout. This is useful for showing examples in
|
||||
some circumstances. Note that regular prompts
|
||||
will automatically echo the input.
|
||||
"""
|
||||
|
||||
def __init__(self, charset=None, env=None, echo_stdin=False):
|
||||
if charset is None:
|
||||
charset = 'utf-8'
|
||||
self.charset = charset
|
||||
self.env = env or {}
|
||||
self.echo_stdin = echo_stdin
|
||||
|
||||
def get_default_prog_name(self, cli):
|
||||
"""Given a command object it will return the default program name
|
||||
for it. The default is the `name` attribute or ``"root"`` if not
|
||||
set.
|
||||
"""
|
||||
return cli.name or 'root'
|
||||
|
||||
def make_env(self, overrides=None):
|
||||
"""Returns the environment overrides for invoking a script."""
|
||||
rv = dict(self.env)
|
||||
if overrides:
|
||||
rv.update(overrides)
|
||||
return rv
|
||||
|
||||
@contextlib.contextmanager
|
||||
def isolation(self, input=None, env=None, color=False):
|
||||
"""A context manager that sets up the isolation for invoking of a
|
||||
command line tool. This sets up stdin with the given input data
|
||||
and `os.environ` with the overrides from the given dictionary.
|
||||
This also rebinds some internals in Click to be mocked (like the
|
||||
prompt functionality).
|
||||
|
||||
This is automatically done in the :meth:`invoke` method.
|
||||
|
||||
.. versionadded:: 4.0
|
||||
The ``color`` parameter was added.
|
||||
|
||||
:param input: the input stream to put into sys.stdin.
|
||||
:param env: the environment overrides as dictionary.
|
||||
:param color: whether the output should contain color codes. The
|
||||
application can still override this explicitly.
|
||||
"""
|
||||
input = make_input_stream(input, self.charset)
|
||||
|
||||
old_stdin = sys.stdin
|
||||
old_stdout = sys.stdout
|
||||
old_stderr = sys.stderr
|
||||
old_forced_width = clickpkg.formatting.FORCED_WIDTH
|
||||
clickpkg.formatting.FORCED_WIDTH = 80
|
||||
|
||||
env = self.make_env(env)
|
||||
|
||||
if PY2:
|
||||
sys.stdout = sys.stderr = bytes_output = StringIO()
|
||||
if self.echo_stdin:
|
||||
input = EchoingStdin(input, bytes_output)
|
||||
else:
|
||||
bytes_output = io.BytesIO()
|
||||
if self.echo_stdin:
|
||||
input = EchoingStdin(input, bytes_output)
|
||||
input = io.TextIOWrapper(input, encoding=self.charset)
|
||||
sys.stdout = sys.stderr = io.TextIOWrapper(
|
||||
bytes_output, encoding=self.charset)
|
||||
|
||||
sys.stdin = input
|
||||
|
||||
def visible_input(prompt=None):
|
||||
sys.stdout.write(prompt or '')
|
||||
val = input.readline().rstrip('\r\n')
|
||||
sys.stdout.write(val + '\n')
|
||||
sys.stdout.flush()
|
||||
return val
|
||||
|
||||
def hidden_input(prompt=None):
|
||||
sys.stdout.write((prompt or '') + '\n')
|
||||
sys.stdout.flush()
|
||||
return input.readline().rstrip('\r\n')
|
||||
|
||||
def _getchar(echo):
|
||||
char = sys.stdin.read(1)
|
||||
if echo:
|
||||
sys.stdout.write(char)
|
||||
sys.stdout.flush()
|
||||
return char
|
||||
|
||||
default_color = color
|
||||
def should_strip_ansi(stream=None, color=None):
|
||||
if color is None:
|
||||
return not default_color
|
||||
return not color
|
||||
|
||||
old_visible_prompt_func = clickpkg.termui.visible_prompt_func
|
||||
old_hidden_prompt_func = clickpkg.termui.hidden_prompt_func
|
||||
old__getchar_func = clickpkg.termui._getchar
|
||||
old_should_strip_ansi = clickpkg.utils.should_strip_ansi
|
||||
clickpkg.termui.visible_prompt_func = visible_input
|
||||
clickpkg.termui.hidden_prompt_func = hidden_input
|
||||
clickpkg.termui._getchar = _getchar
|
||||
clickpkg.utils.should_strip_ansi = should_strip_ansi
|
||||
|
||||
old_env = {}
|
||||
try:
|
||||
for key, value in iteritems(env):
|
||||
old_env[key] = os.environ.get(key)
|
||||
if value is None:
|
||||
try:
|
||||
del os.environ[key]
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
os.environ[key] = value
|
||||
yield bytes_output
|
||||
finally:
|
||||
for key, value in iteritems(old_env):
|
||||
if value is None:
|
||||
try:
|
||||
del os.environ[key]
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
os.environ[key] = value
|
||||
sys.stdout = old_stdout
|
||||
sys.stderr = old_stderr
|
||||
sys.stdin = old_stdin
|
||||
clickpkg.termui.visible_prompt_func = old_visible_prompt_func
|
||||
clickpkg.termui.hidden_prompt_func = old_hidden_prompt_func
|
||||
clickpkg.termui._getchar = old__getchar_func
|
||||
clickpkg.utils.should_strip_ansi = old_should_strip_ansi
|
||||
clickpkg.formatting.FORCED_WIDTH = old_forced_width
|
||||
|
||||
def invoke(self, cli, args=None, input=None, env=None,
|
||||
catch_exceptions=True, color=False, **extra):
|
||||
"""Invokes a command in an isolated environment. The arguments are
|
||||
forwarded directly to the command line script, the `extra` keyword
|
||||
arguments are passed to the :meth:`~clickpkg.Command.main` function of
|
||||
the command.
|
||||
|
||||
This returns a :class:`Result` object.
|
||||
|
||||
.. versionadded:: 3.0
|
||||
The ``catch_exceptions`` parameter was added.
|
||||
|
||||
.. versionchanged:: 3.0
|
||||
The result object now has an `exc_info` attribute with the
|
||||
traceback if available.
|
||||
|
||||
.. versionadded:: 4.0
|
||||
The ``color`` parameter was added.
|
||||
|
||||
:param cli: the command to invoke
|
||||
:param args: the arguments to invoke
|
||||
:param input: the input data for `sys.stdin`.
|
||||
:param env: the environment overrides.
|
||||
:param catch_exceptions: Whether to catch any other exceptions than
|
||||
``SystemExit``.
|
||||
:param extra: the keyword arguments to pass to :meth:`main`.
|
||||
:param color: whether the output should contain color codes. The
|
||||
application can still override this explicitly.
|
||||
"""
|
||||
exc_info = None
|
||||
with self.isolation(input=input, env=env, color=color) as out:
|
||||
exception = None
|
||||
exit_code = 0
|
||||
|
||||
try:
|
||||
cli.main(args=args or (),
|
||||
prog_name=self.get_default_prog_name(cli), **extra)
|
||||
except SystemExit as e:
|
||||
if e.code != 0:
|
||||
exception = e
|
||||
|
||||
exc_info = sys.exc_info()
|
||||
|
||||
exit_code = e.code
|
||||
if not isinstance(exit_code, int):
|
||||
sys.stdout.write(str(exit_code))
|
||||
sys.stdout.write('\n')
|
||||
exit_code = 1
|
||||
except Exception as e:
|
||||
if not catch_exceptions:
|
||||
raise
|
||||
exception = e
|
||||
exit_code = -1
|
||||
exc_info = sys.exc_info()
|
||||
finally:
|
||||
sys.stdout.flush()
|
||||
output = out.getvalue()
|
||||
|
||||
return Result(runner=self,
|
||||
output_bytes=output,
|
||||
exit_code=exit_code,
|
||||
exception=exception,
|
||||
exc_info=exc_info)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def isolated_filesystem(self):
|
||||
"""A context manager that creates a temporary folder and changes
|
||||
the current working directory to it for isolated filesystem tests.
|
||||
"""
|
||||
cwd = os.getcwd()
|
||||
t = tempfile.mkdtemp()
|
||||
os.chdir(t)
|
||||
try:
|
||||
yield t
|
||||
finally:
|
||||
os.chdir(cwd)
|
||||
try:
|
||||
shutil.rmtree(t)
|
||||
except (OSError, IOError):
|
||||
pass
|
||||
550
click/types.py
550
click/types.py
@@ -1,550 +0,0 @@
|
||||
import os
|
||||
import stat
|
||||
|
||||
from ._compat import open_stream, text_type, filename_to_ui, \
|
||||
get_filesystem_encoding, get_streerror, _get_argv_encoding, PY2
|
||||
from .exceptions import BadParameter
|
||||
from .utils import safecall, LazyFile
|
||||
|
||||
|
||||
class ParamType(object):
|
||||
"""Helper for converting values through types. The following is
|
||||
necessary for a valid type:
|
||||
|
||||
* it needs a name
|
||||
* it needs to pass through None unchanged
|
||||
* it needs to convert from a string
|
||||
* it needs to convert its result type through unchanged
|
||||
(eg: needs to be idempotent)
|
||||
* it needs to be able to deal with param and context being `None`.
|
||||
This can be the case when the object is used with prompt
|
||||
inputs.
|
||||
"""
|
||||
is_composite = False
|
||||
|
||||
#: the descriptive name of this type
|
||||
name = None
|
||||
|
||||
#: if a list of this type is expected and the value is pulled from a
|
||||
#: string environment variable, this is what splits it up. `None`
|
||||
#: means any whitespace. For all parameters the general rule is that
|
||||
#: whitespace splits them up. The exception are paths and files which
|
||||
#: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on
|
||||
#: Windows).
|
||||
envvar_list_splitter = None
|
||||
|
||||
def __call__(self, value, param=None, ctx=None):
|
||||
if value is not None:
|
||||
return self.convert(value, param, ctx)
|
||||
|
||||
def get_metavar(self, param):
|
||||
"""Returns the metavar default for this param if it provides one."""
|
||||
|
||||
def get_missing_message(self, param):
|
||||
"""Optionally might return extra information about a missing
|
||||
parameter.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
"""Converts the value. This is not invoked for values that are
|
||||
`None` (the missing value).
|
||||
"""
|
||||
return value
|
||||
|
||||
def split_envvar_value(self, rv):
|
||||
"""Given a value from an environment variable this splits it up
|
||||
into small chunks depending on the defined envvar list splitter.
|
||||
|
||||
If the splitter is set to `None`, which means that whitespace splits,
|
||||
then leading and trailing whitespace is ignored. Otherwise, leading
|
||||
and trailing splitters usually lead to empty items being included.
|
||||
"""
|
||||
return (rv or '').split(self.envvar_list_splitter)
|
||||
|
||||
def fail(self, message, param=None, ctx=None):
|
||||
"""Helper method to fail with an invalid value message."""
|
||||
raise BadParameter(message, ctx=ctx, param=param)
|
||||
|
||||
|
||||
class CompositeParamType(ParamType):
|
||||
is_composite = True
|
||||
|
||||
@property
|
||||
def arity(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class FuncParamType(ParamType):
|
||||
|
||||
def __init__(self, func):
|
||||
self.name = func.__name__
|
||||
self.func = func
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
try:
|
||||
return self.func(value)
|
||||
except ValueError:
|
||||
try:
|
||||
value = text_type(value)
|
||||
except UnicodeError:
|
||||
value = str(value).decode('utf-8', 'replace')
|
||||
self.fail(value, param, ctx)
|
||||
|
||||
|
||||
class UnprocessedParamType(ParamType):
|
||||
name = 'text'
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
return value
|
||||
|
||||
def __repr__(self):
|
||||
return 'UNPROCESSED'
|
||||
|
||||
|
||||
class StringParamType(ParamType):
|
||||
name = 'text'
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
if isinstance(value, bytes):
|
||||
enc = _get_argv_encoding()
|
||||
try:
|
||||
value = value.decode(enc)
|
||||
except UnicodeError:
|
||||
fs_enc = get_filesystem_encoding()
|
||||
if fs_enc != enc:
|
||||
try:
|
||||
value = value.decode(fs_enc)
|
||||
except UnicodeError:
|
||||
value = value.decode('utf-8', 'replace')
|
||||
return value
|
||||
return value
|
||||
|
||||
def __repr__(self):
|
||||
return 'STRING'
|
||||
|
||||
|
||||
class Choice(ParamType):
|
||||
"""The choice type allows a value to be checked against a fixed set of
|
||||
supported values. All of these values have to be strings.
|
||||
|
||||
See :ref:`choice-opts` for an example.
|
||||
"""
|
||||
name = 'choice'
|
||||
|
||||
def __init__(self, choices):
|
||||
self.choices = choices
|
||||
|
||||
def get_metavar(self, param):
|
||||
return '[%s]' % '|'.join(self.choices)
|
||||
|
||||
def get_missing_message(self, param):
|
||||
return 'Choose from %s.' % ', '.join(self.choices)
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
# Exact match
|
||||
if value in self.choices:
|
||||
return value
|
||||
|
||||
# Match through normalization
|
||||
if ctx is not None and \
|
||||
ctx.token_normalize_func is not None:
|
||||
value = ctx.token_normalize_func(value)
|
||||
for choice in self.choices:
|
||||
if ctx.token_normalize_func(choice) == value:
|
||||
return choice
|
||||
|
||||
self.fail('invalid choice: %s. (choose from %s)' %
|
||||
(value, ', '.join(self.choices)), param, ctx)
|
||||
|
||||
def __repr__(self):
|
||||
return 'Choice(%r)' % list(self.choices)
|
||||
|
||||
|
||||
class IntParamType(ParamType):
|
||||
name = 'integer'
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
try:
|
||||
return int(value)
|
||||
except (ValueError, UnicodeError):
|
||||
self.fail('%s is not a valid integer' % value, param, ctx)
|
||||
|
||||
def __repr__(self):
|
||||
return 'INT'
|
||||
|
||||
|
||||
class IntRange(IntParamType):
|
||||
"""A parameter that works similar to :data:`click.INT` but restricts
|
||||
the value to fit into a range. The default behavior is to fail if the
|
||||
value falls outside the range, but it can also be silently clamped
|
||||
between the two edges.
|
||||
|
||||
See :ref:`ranges` for an example.
|
||||
"""
|
||||
name = 'integer range'
|
||||
|
||||
def __init__(self, min=None, max=None, clamp=False):
|
||||
self.min = min
|
||||
self.max = max
|
||||
self.clamp = clamp
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
rv = IntParamType.convert(self, value, param, ctx)
|
||||
if self.clamp:
|
||||
if self.min is not None and rv < self.min:
|
||||
return self.min
|
||||
if self.max is not None and rv > self.max:
|
||||
return self.max
|
||||
if self.min is not None and rv < self.min or \
|
||||
self.max is not None and rv > self.max:
|
||||
if self.min is None:
|
||||
self.fail('%s is bigger than the maximum valid value '
|
||||
'%s.' % (rv, self.max), param, ctx)
|
||||
elif self.max is None:
|
||||
self.fail('%s is smaller than the minimum valid value '
|
||||
'%s.' % (rv, self.min), param, ctx)
|
||||
else:
|
||||
self.fail('%s is not in the valid range of %s to %s.'
|
||||
% (rv, self.min, self.max), param, ctx)
|
||||
return rv
|
||||
|
||||
def __repr__(self):
|
||||
return 'IntRange(%r, %r)' % (self.min, self.max)
|
||||
|
||||
|
||||
class BoolParamType(ParamType):
|
||||
name = 'boolean'
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
if isinstance(value, bool):
|
||||
return bool(value)
|
||||
value = value.lower()
|
||||
if value in ('true', '1', 'yes', 'y'):
|
||||
return True
|
||||
elif value in ('false', '0', 'no', 'n'):
|
||||
return False
|
||||
self.fail('%s is not a valid boolean' % value, param, ctx)
|
||||
|
||||
def __repr__(self):
|
||||
return 'BOOL'
|
||||
|
||||
|
||||
class FloatParamType(ParamType):
|
||||
name = 'float'
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
try:
|
||||
return float(value)
|
||||
except (UnicodeError, ValueError):
|
||||
self.fail('%s is not a valid floating point value' %
|
||||
value, param, ctx)
|
||||
|
||||
def __repr__(self):
|
||||
return 'FLOAT'
|
||||
|
||||
|
||||
class UUIDParameterType(ParamType):
|
||||
name = 'uuid'
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
import uuid
|
||||
try:
|
||||
if PY2 and isinstance(value, text_type):
|
||||
value = value.encode('ascii')
|
||||
return uuid.UUID(value)
|
||||
except (UnicodeError, ValueError):
|
||||
self.fail('%s is not a valid UUID value' % value, param, ctx)
|
||||
|
||||
def __repr__(self):
|
||||
return 'UUID'
|
||||
|
||||
|
||||
class File(ParamType):
|
||||
"""Declares a parameter to be a file for reading or writing. The file
|
||||
is automatically closed once the context tears down (after the command
|
||||
finished working).
|
||||
|
||||
Files can be opened for reading or writing. The special value ``-``
|
||||
indicates stdin or stdout depending on the mode.
|
||||
|
||||
By default, the file is opened for reading text data, but it can also be
|
||||
opened in binary mode or for writing. The encoding parameter can be used
|
||||
to force a specific encoding.
|
||||
|
||||
The `lazy` flag controls if the file should be opened immediately or
|
||||
upon first IO. The default is to be non lazy for standard input and
|
||||
output streams as well as files opened for reading, lazy otherwise.
|
||||
|
||||
Starting with Click 2.0, files can also be opened atomically in which
|
||||
case all writes go into a separate file in the same folder and upon
|
||||
completion the file will be moved over to the original location. This
|
||||
is useful if a file regularly read by other users is modified.
|
||||
|
||||
See :ref:`file-args` for more information.
|
||||
"""
|
||||
name = 'filename'
|
||||
envvar_list_splitter = os.path.pathsep
|
||||
|
||||
def __init__(self, mode='r', encoding=None, errors='strict', lazy=None,
|
||||
atomic=False):
|
||||
self.mode = mode
|
||||
self.encoding = encoding
|
||||
self.errors = errors
|
||||
self.lazy = lazy
|
||||
self.atomic = atomic
|
||||
|
||||
def resolve_lazy_flag(self, value):
|
||||
if self.lazy is not None:
|
||||
return self.lazy
|
||||
if value == '-':
|
||||
return False
|
||||
elif 'w' in self.mode:
|
||||
return True
|
||||
return False
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
try:
|
||||
if hasattr(value, 'read') or hasattr(value, 'write'):
|
||||
return value
|
||||
|
||||
lazy = self.resolve_lazy_flag(value)
|
||||
|
||||
if lazy:
|
||||
f = LazyFile(value, self.mode, self.encoding, self.errors,
|
||||
atomic=self.atomic)
|
||||
if ctx is not None:
|
||||
ctx.call_on_close(f.close_intelligently)
|
||||
return f
|
||||
|
||||
f, should_close = open_stream(value, self.mode,
|
||||
self.encoding, self.errors,
|
||||
atomic=self.atomic)
|
||||
# If a context is provided, we automatically close the file
|
||||
# at the end of the context execution (or flush out). If a
|
||||
# context does not exist, it's the caller's responsibility to
|
||||
# properly close the file. This for instance happens when the
|
||||
# type is used with prompts.
|
||||
if ctx is not None:
|
||||
if should_close:
|
||||
ctx.call_on_close(safecall(f.close))
|
||||
else:
|
||||
ctx.call_on_close(safecall(f.flush))
|
||||
return f
|
||||
except (IOError, OSError) as e:
|
||||
self.fail('Could not open file: %s: %s' % (
|
||||
filename_to_ui(value),
|
||||
get_streerror(e),
|
||||
), param, ctx)
|
||||
|
||||
|
||||
class Path(ParamType):
|
||||
"""The path type is similar to the :class:`File` type but it performs
|
||||
different checks. First of all, instead of returning an open file
|
||||
handle it returns just the filename. Secondly, it can perform various
|
||||
basic checks about what the file or directory should be.
|
||||
|
||||
.. versionchanged:: 6.0
|
||||
`allow_dash` was added.
|
||||
|
||||
:param exists: if set to true, the file or directory needs to exist for
|
||||
this value to be valid. If this is not required and a
|
||||
file does indeed not exist, then all further checks are
|
||||
silently skipped.
|
||||
:param file_okay: controls if a file is a possible value.
|
||||
:param dir_okay: controls if a directory is a possible value.
|
||||
:param writable: if true, a writable check is performed.
|
||||
:param readable: if true, a readable check is performed.
|
||||
:param resolve_path: if this is true, then the path is fully resolved
|
||||
before the value is passed onwards. This means
|
||||
that it's absolute and symlinks are resolved.
|
||||
:param allow_dash: If this is set to `True`, a single dash to indicate
|
||||
standard streams is permitted.
|
||||
:param type: optionally a string type that should be used to
|
||||
represent the path. The default is `None` which
|
||||
means the return value will be either bytes or
|
||||
unicode depending on what makes most sense given the
|
||||
input data Click deals with.
|
||||
"""
|
||||
envvar_list_splitter = os.path.pathsep
|
||||
|
||||
def __init__(self, exists=False, file_okay=True, dir_okay=True,
|
||||
writable=False, readable=True, resolve_path=False,
|
||||
allow_dash=False, path_type=None):
|
||||
self.exists = exists
|
||||
self.file_okay = file_okay
|
||||
self.dir_okay = dir_okay
|
||||
self.writable = writable
|
||||
self.readable = readable
|
||||
self.resolve_path = resolve_path
|
||||
self.allow_dash = allow_dash
|
||||
self.type = path_type
|
||||
|
||||
if self.file_okay and not self.dir_okay:
|
||||
self.name = 'file'
|
||||
self.path_type = 'File'
|
||||
if self.dir_okay and not self.file_okay:
|
||||
self.name = 'directory'
|
||||
self.path_type = 'Directory'
|
||||
else:
|
||||
self.name = 'path'
|
||||
self.path_type = 'Path'
|
||||
|
||||
def coerce_path_result(self, rv):
|
||||
if self.type is not None and not isinstance(rv, self.type):
|
||||
if self.type is text_type:
|
||||
rv = rv.decode(get_filesystem_encoding())
|
||||
else:
|
||||
rv = rv.encode(get_filesystem_encoding())
|
||||
return rv
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
rv = value
|
||||
|
||||
is_dash = self.file_okay and self.allow_dash and rv in (b'-', '-')
|
||||
|
||||
if not is_dash:
|
||||
if self.resolve_path:
|
||||
rv = os.path.realpath(rv)
|
||||
|
||||
try:
|
||||
st = os.stat(rv)
|
||||
except OSError:
|
||||
if not self.exists:
|
||||
return self.coerce_path_result(rv)
|
||||
self.fail('%s "%s" does not exist.' % (
|
||||
self.path_type,
|
||||
filename_to_ui(value)
|
||||
), param, ctx)
|
||||
|
||||
if not self.file_okay and stat.S_ISREG(st.st_mode):
|
||||
self.fail('%s "%s" is a file.' % (
|
||||
self.path_type,
|
||||
filename_to_ui(value)
|
||||
), param, ctx)
|
||||
if not self.dir_okay and stat.S_ISDIR(st.st_mode):
|
||||
self.fail('%s "%s" is a directory.' % (
|
||||
self.path_type,
|
||||
filename_to_ui(value)
|
||||
), param, ctx)
|
||||
if self.writable and not os.access(value, os.W_OK):
|
||||
self.fail('%s "%s" is not writable.' % (
|
||||
self.path_type,
|
||||
filename_to_ui(value)
|
||||
), param, ctx)
|
||||
if self.readable and not os.access(value, os.R_OK):
|
||||
self.fail('%s "%s" is not readable.' % (
|
||||
self.path_type,
|
||||
filename_to_ui(value)
|
||||
), param, ctx)
|
||||
|
||||
return self.coerce_path_result(rv)
|
||||
|
||||
|
||||
class Tuple(CompositeParamType):
|
||||
"""The default behavior of Click is to apply a type on a value directly.
|
||||
This works well in most cases, except for when `nargs` is set to a fixed
|
||||
count and different types should be used for different items. In this
|
||||
case the :class:`Tuple` type can be used. This type can only be used
|
||||
if `nargs` is set to a fixed number.
|
||||
|
||||
For more information see :ref:`tuple-type`.
|
||||
|
||||
This can be selected by using a Python tuple literal as a type.
|
||||
|
||||
:param types: a list of types that should be used for the tuple items.
|
||||
"""
|
||||
|
||||
def __init__(self, types):
|
||||
self.types = [convert_type(ty) for ty in types]
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return "<" + " ".join(ty.name for ty in self.types) + ">"
|
||||
|
||||
@property
|
||||
def arity(self):
|
||||
return len(self.types)
|
||||
|
||||
def convert(self, value, param, ctx):
|
||||
if len(value) != len(self.types):
|
||||
raise TypeError('It would appear that nargs is set to conflict '
|
||||
'with the composite type arity.')
|
||||
return tuple(ty(x, param, ctx) for ty, x in zip(self.types, value))
|
||||
|
||||
|
||||
def convert_type(ty, default=None):
|
||||
"""Converts a callable or python ty into the most appropriate param
|
||||
ty.
|
||||
"""
|
||||
guessed_type = False
|
||||
if ty is None and default is not None:
|
||||
if isinstance(default, tuple):
|
||||
ty = tuple(map(type, default))
|
||||
else:
|
||||
ty = type(default)
|
||||
guessed_type = True
|
||||
|
||||
if isinstance(ty, tuple):
|
||||
return Tuple(ty)
|
||||
if isinstance(ty, ParamType):
|
||||
return ty
|
||||
if ty is text_type or ty is str or ty is None:
|
||||
return STRING
|
||||
if ty is int:
|
||||
return INT
|
||||
# Booleans are only okay if not guessed. This is done because for
|
||||
# flags the default value is actually a bit of a lie in that it
|
||||
# indicates which of the flags is the one we want. See get_default()
|
||||
# for more information.
|
||||
if ty is bool and not guessed_type:
|
||||
return BOOL
|
||||
if ty is float:
|
||||
return FLOAT
|
||||
if guessed_type:
|
||||
return STRING
|
||||
|
||||
# Catch a common mistake
|
||||
if __debug__:
|
||||
try:
|
||||
if issubclass(ty, ParamType):
|
||||
raise AssertionError('Attempted to use an uninstantiated '
|
||||
'parameter type (%s).' % ty)
|
||||
except TypeError:
|
||||
pass
|
||||
return FuncParamType(ty)
|
||||
|
||||
|
||||
#: A dummy parameter type that just does nothing. From a user's
|
||||
#: perspective this appears to just be the same as `STRING` but internally
|
||||
#: no string conversion takes place. This is necessary to achieve the
|
||||
#: same bytes/unicode behavior on Python 2/3 in situations where you want
|
||||
#: to not convert argument types. This is usually useful when working
|
||||
#: with file paths as they can appear in bytes and unicode.
|
||||
#:
|
||||
#: For path related uses the :class:`Path` type is a better choice but
|
||||
#: there are situations where an unprocessed type is useful which is why
|
||||
#: it is is provided.
|
||||
#:
|
||||
#: .. versionadded:: 4.0
|
||||
UNPROCESSED = UnprocessedParamType()
|
||||
|
||||
#: A unicode string parameter type which is the implicit default. This
|
||||
#: can also be selected by using ``str`` as type.
|
||||
STRING = StringParamType()
|
||||
|
||||
#: An integer parameter. This can also be selected by using ``int`` as
|
||||
#: type.
|
||||
INT = IntParamType()
|
||||
|
||||
#: A floating point value parameter. This can also be selected by using
|
||||
#: ``float`` as type.
|
||||
FLOAT = FloatParamType()
|
||||
|
||||
#: A boolean parameter. This is the default for boolean flags. This can
|
||||
#: also be selected by using ``bool`` as a type.
|
||||
BOOL = BoolParamType()
|
||||
|
||||
#: A UUID parameter.
|
||||
UUID = UUIDParameterType()
|
||||
415
click/utils.py
415
click/utils.py
@@ -1,415 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
from .globals import resolve_color_default
|
||||
|
||||
from ._compat import text_type, open_stream, get_filesystem_encoding, \
|
||||
get_streerror, string_types, PY2, binary_streams, text_streams, \
|
||||
filename_to_ui, auto_wrap_for_ansi, strip_ansi, should_strip_ansi, \
|
||||
_default_text_stdout, _default_text_stderr, is_bytes, WIN
|
||||
|
||||
if not PY2:
|
||||
from ._compat import _find_binary_writer
|
||||
elif WIN:
|
||||
from ._winconsole import _get_windows_argv, \
|
||||
_hash_py_argv, _initial_argv_hash
|
||||
|
||||
|
||||
echo_native_types = string_types + (bytes, bytearray)
|
||||
|
||||
|
||||
def _posixify(name):
|
||||
return '-'.join(name.split()).lower()
|
||||
|
||||
|
||||
def safecall(func):
|
||||
"""Wraps a function so that it swallows exceptions."""
|
||||
def wrapper(*args, **kwargs):
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
except Exception:
|
||||
pass
|
||||
return wrapper
|
||||
|
||||
|
||||
def make_str(value):
|
||||
"""Converts a value into a valid string."""
|
||||
if isinstance(value, bytes):
|
||||
try:
|
||||
return value.decode(get_filesystem_encoding())
|
||||
except UnicodeError:
|
||||
return value.decode('utf-8', 'replace')
|
||||
return text_type(value)
|
||||
|
||||
|
||||
def make_default_short_help(help, max_length=45):
|
||||
words = help.split()
|
||||
total_length = 0
|
||||
result = []
|
||||
done = False
|
||||
|
||||
for word in words:
|
||||
if word[-1:] == '.':
|
||||
done = True
|
||||
new_length = result and 1 + len(word) or len(word)
|
||||
if total_length + new_length > max_length:
|
||||
result.append('...')
|
||||
done = True
|
||||
else:
|
||||
if result:
|
||||
result.append(' ')
|
||||
result.append(word)
|
||||
if done:
|
||||
break
|
||||
total_length += new_length
|
||||
|
||||
return ''.join(result)
|
||||
|
||||
|
||||
class LazyFile(object):
|
||||
"""A lazy file works like a regular file but it does not fully open
|
||||
the file but it does perform some basic checks early to see if the
|
||||
filename parameter does make sense. This is useful for safely opening
|
||||
files for writing.
|
||||
"""
|
||||
|
||||
def __init__(self, filename, mode='r', encoding=None, errors='strict',
|
||||
atomic=False):
|
||||
self.name = filename
|
||||
self.mode = mode
|
||||
self.encoding = encoding
|
||||
self.errors = errors
|
||||
self.atomic = atomic
|
||||
|
||||
if filename == '-':
|
||||
self._f, self.should_close = open_stream(filename, mode,
|
||||
encoding, errors)
|
||||
else:
|
||||
if 'r' in mode:
|
||||
# Open and close the file in case we're opening it for
|
||||
# reading so that we can catch at least some errors in
|
||||
# some cases early.
|
||||
open(filename, mode).close()
|
||||
self._f = None
|
||||
self.should_close = True
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.open(), name)
|
||||
|
||||
def __repr__(self):
|
||||
if self._f is not None:
|
||||
return repr(self._f)
|
||||
return '<unopened file %r %s>' % (self.name, self.mode)
|
||||
|
||||
def open(self):
|
||||
"""Opens the file if it's not yet open. This call might fail with
|
||||
a :exc:`FileError`. Not handling this error will produce an error
|
||||
that Click shows.
|
||||
"""
|
||||
if self._f is not None:
|
||||
return self._f
|
||||
try:
|
||||
rv, self.should_close = open_stream(self.name, self.mode,
|
||||
self.encoding,
|
||||
self.errors,
|
||||
atomic=self.atomic)
|
||||
except (IOError, OSError) as e:
|
||||
from .exceptions import FileError
|
||||
raise FileError(self.name, hint=get_streerror(e))
|
||||
self._f = rv
|
||||
return rv
|
||||
|
||||
def close(self):
|
||||
"""Closes the underlying file, no matter what."""
|
||||
if self._f is not None:
|
||||
self._f.close()
|
||||
|
||||
def close_intelligently(self):
|
||||
"""This function only closes the file if it was opened by the lazy
|
||||
file wrapper. For instance this will never close stdin.
|
||||
"""
|
||||
if self.should_close:
|
||||
self.close()
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, tb):
|
||||
self.close_intelligently()
|
||||
|
||||
def __iter__(self):
|
||||
self.open()
|
||||
return iter(self._f)
|
||||
|
||||
|
||||
class KeepOpenFile(object):
|
||||
|
||||
def __init__(self, file):
|
||||
self._file = file
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self._file, name)
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, tb):
|
||||
pass
|
||||
|
||||
def __repr__(self):
|
||||
return repr(self._file)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._file)
|
||||
|
||||
|
||||
def echo(message=None, file=None, nl=True, err=False, color=None):
|
||||
"""Prints a message plus a newline to the given file or stdout. On
|
||||
first sight, this looks like the print function, but it has improved
|
||||
support for handling Unicode and binary data that does not fail no
|
||||
matter how badly configured the system is.
|
||||
|
||||
Primarily it means that you can print binary data as well as Unicode
|
||||
data on both 2.x and 3.x to the given file in the most appropriate way
|
||||
possible. This is a very carefree function as in that it will try its
|
||||
best to not fail. As of Click 6.0 this includes support for unicode
|
||||
output on the Windows console.
|
||||
|
||||
In addition to that, if `colorama`_ is installed, the echo function will
|
||||
also support clever handling of ANSI codes. Essentially it will then
|
||||
do the following:
|
||||
|
||||
- add transparent handling of ANSI color codes on Windows.
|
||||
- hide ANSI codes automatically if the destination file is not a
|
||||
terminal.
|
||||
|
||||
.. _colorama: http://pypi.python.org/pypi/colorama
|
||||
|
||||
.. versionchanged:: 6.0
|
||||
As of Click 6.0 the echo function will properly support unicode
|
||||
output on the windows console. Not that click does not modify
|
||||
the interpreter in any way which means that `sys.stdout` or the
|
||||
print statement or function will still not provide unicode support.
|
||||
|
||||
.. versionchanged:: 2.0
|
||||
Starting with version 2.0 of Click, the echo function will work
|
||||
with colorama if it's installed.
|
||||
|
||||
.. versionadded:: 3.0
|
||||
The `err` parameter was added.
|
||||
|
||||
.. versionchanged:: 4.0
|
||||
Added the `color` flag.
|
||||
|
||||
:param message: the message to print
|
||||
:param file: the file to write to (defaults to ``stdout``)
|
||||
:param err: if set to true the file defaults to ``stderr`` instead of
|
||||
``stdout``. This is faster and easier than calling
|
||||
:func:`get_text_stderr` yourself.
|
||||
:param nl: if set to `True` (the default) a newline is printed afterwards.
|
||||
:param color: controls if the terminal supports ANSI colors or not. The
|
||||
default is autodetection.
|
||||
"""
|
||||
if file is None:
|
||||
if err:
|
||||
file = _default_text_stderr()
|
||||
else:
|
||||
file = _default_text_stdout()
|
||||
|
||||
# Convert non bytes/text into the native string type.
|
||||
if message is not None and not isinstance(message, echo_native_types):
|
||||
message = text_type(message)
|
||||
|
||||
if nl:
|
||||
message = message or u''
|
||||
if isinstance(message, text_type):
|
||||
message += u'\n'
|
||||
else:
|
||||
message += b'\n'
|
||||
|
||||
# If there is a message, and we're in Python 3, and the value looks
|
||||
# like bytes, we manually need to find the binary stream and write the
|
||||
# message in there. This is done separately so that most stream
|
||||
# types will work as you would expect. Eg: you can write to StringIO
|
||||
# for other cases.
|
||||
if message and not PY2 and is_bytes(message):
|
||||
binary_file = _find_binary_writer(file)
|
||||
if binary_file is not None:
|
||||
file.flush()
|
||||
binary_file.write(message)
|
||||
binary_file.flush()
|
||||
return
|
||||
|
||||
# ANSI-style support. If there is no message or we are dealing with
|
||||
# bytes nothing is happening. If we are connected to a file we want
|
||||
# to strip colors. If we are on windows we either wrap the stream
|
||||
# to strip the color or we use the colorama support to translate the
|
||||
# ansi codes to API calls.
|
||||
if message and not is_bytes(message):
|
||||
color = resolve_color_default(color)
|
||||
if should_strip_ansi(file, color):
|
||||
message = strip_ansi(message)
|
||||
elif WIN:
|
||||
if auto_wrap_for_ansi is not None:
|
||||
file = auto_wrap_for_ansi(file)
|
||||
elif not color:
|
||||
message = strip_ansi(message)
|
||||
|
||||
if message:
|
||||
file.write(message)
|
||||
file.flush()
|
||||
|
||||
|
||||
def get_binary_stream(name):
|
||||
"""Returns a system stream for byte processing. This essentially
|
||||
returns the stream from the sys module with the given name but it
|
||||
solves some compatibility issues between different Python versions.
|
||||
Primarily this function is necessary for getting binary streams on
|
||||
Python 3.
|
||||
|
||||
:param name: the name of the stream to open. Valid names are ``'stdin'``,
|
||||
``'stdout'`` and ``'stderr'``
|
||||
"""
|
||||
opener = binary_streams.get(name)
|
||||
if opener is None:
|
||||
raise TypeError('Unknown standard stream %r' % name)
|
||||
return opener()
|
||||
|
||||
|
||||
def get_text_stream(name, encoding=None, errors='strict'):
|
||||
"""Returns a system stream for text processing. This usually returns
|
||||
a wrapped stream around a binary stream returned from
|
||||
:func:`get_binary_stream` but it also can take shortcuts on Python 3
|
||||
for already correctly configured streams.
|
||||
|
||||
:param name: the name of the stream to open. Valid names are ``'stdin'``,
|
||||
``'stdout'`` and ``'stderr'``
|
||||
:param encoding: overrides the detected default encoding.
|
||||
:param errors: overrides the default error mode.
|
||||
"""
|
||||
opener = text_streams.get(name)
|
||||
if opener is None:
|
||||
raise TypeError('Unknown standard stream %r' % name)
|
||||
return opener(encoding, errors)
|
||||
|
||||
|
||||
def open_file(filename, mode='r', encoding=None, errors='strict',
|
||||
lazy=False, atomic=False):
|
||||
"""This is similar to how the :class:`File` works but for manual
|
||||
usage. Files are opened non lazy by default. This can open regular
|
||||
files as well as stdin/stdout if ``'-'`` is passed.
|
||||
|
||||
If stdin/stdout is returned the stream is wrapped so that the context
|
||||
manager will not close the stream accidentally. This makes it possible
|
||||
to always use the function like this without having to worry to
|
||||
accidentally close a standard stream::
|
||||
|
||||
with open_file(filename) as f:
|
||||
...
|
||||
|
||||
.. versionadded:: 3.0
|
||||
|
||||
:param filename: the name of the file to open (or ``'-'`` for stdin/stdout).
|
||||
:param mode: the mode in which to open the file.
|
||||
:param encoding: the encoding to use.
|
||||
:param errors: the error handling for this file.
|
||||
:param lazy: can be flipped to true to open the file lazily.
|
||||
:param atomic: in atomic mode writes go into a temporary file and it's
|
||||
moved on close.
|
||||
"""
|
||||
if lazy:
|
||||
return LazyFile(filename, mode, encoding, errors, atomic=atomic)
|
||||
f, should_close = open_stream(filename, mode, encoding, errors,
|
||||
atomic=atomic)
|
||||
if not should_close:
|
||||
f = KeepOpenFile(f)
|
||||
return f
|
||||
|
||||
|
||||
def get_os_args():
|
||||
"""This returns the argument part of sys.argv in the most appropriate
|
||||
form for processing. What this means is that this return value is in
|
||||
a format that works for Click to process but does not necessarily
|
||||
correspond well to what's actually standard for the interpreter.
|
||||
|
||||
On most environments the return value is ``sys.argv[:1]`` unchanged.
|
||||
However if you are on Windows and running Python 2 the return value
|
||||
will actually be a list of unicode strings instead because the
|
||||
default behavior on that platform otherwise will not be able to
|
||||
carry all possible values that sys.argv can have.
|
||||
|
||||
.. versionadded:: 6.0
|
||||
"""
|
||||
# We can only extract the unicode argv if sys.argv has not been
|
||||
# changed since the startup of the application.
|
||||
if PY2 and WIN and _initial_argv_hash == _hash_py_argv():
|
||||
return _get_windows_argv()
|
||||
return sys.argv[1:]
|
||||
|
||||
|
||||
def format_filename(filename, shorten=False):
|
||||
"""Formats a filename for user display. The main purpose of this
|
||||
function is to ensure that the filename can be displayed at all. This
|
||||
will decode the filename to unicode if necessary in a way that it will
|
||||
not fail. Optionally, it can shorten the filename to not include the
|
||||
full path to the filename.
|
||||
|
||||
:param filename: formats a filename for UI display. This will also convert
|
||||
the filename into unicode without failing.
|
||||
:param shorten: this optionally shortens the filename to strip of the
|
||||
path that leads up to it.
|
||||
"""
|
||||
if shorten:
|
||||
filename = os.path.basename(filename)
|
||||
return filename_to_ui(filename)
|
||||
|
||||
|
||||
def get_app_dir(app_name, roaming=True, force_posix=False):
|
||||
r"""Returns the config folder for the application. The default behavior
|
||||
is to return whatever is most appropriate for the operating system.
|
||||
|
||||
To give you an idea, for an app called ``"Foo Bar"``, something like
|
||||
the following folders could be returned:
|
||||
|
||||
Mac OS X:
|
||||
``~/Library/Application Support/Foo Bar``
|
||||
Mac OS X (POSIX):
|
||||
``~/.foo-bar``
|
||||
Unix:
|
||||
``~/.config/foo-bar``
|
||||
Unix (POSIX):
|
||||
``~/.foo-bar``
|
||||
Win XP (roaming):
|
||||
``C:\Documents and Settings\<user>\Local Settings\Application Data\Foo Bar``
|
||||
Win XP (not roaming):
|
||||
``C:\Documents and Settings\<user>\Application Data\Foo Bar``
|
||||
Win 7 (roaming):
|
||||
``C:\Users\<user>\AppData\Roaming\Foo Bar``
|
||||
Win 7 (not roaming):
|
||||
``C:\Users\<user>\AppData\Local\Foo Bar``
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
:param app_name: the application name. This should be properly capitalized
|
||||
and can contain whitespace.
|
||||
:param roaming: controls if the folder should be roaming or not on Windows.
|
||||
Has no affect otherwise.
|
||||
:param force_posix: if this is set to `True` then on any POSIX system the
|
||||
folder will be stored in the home folder with a leading
|
||||
dot instead of the XDG config home or darwin's
|
||||
application support folder.
|
||||
"""
|
||||
if WIN:
|
||||
key = roaming and 'APPDATA' or 'LOCALAPPDATA'
|
||||
folder = os.environ.get(key)
|
||||
if folder is None:
|
||||
folder = os.path.expanduser('~')
|
||||
return os.path.join(folder, app_name)
|
||||
if force_posix:
|
||||
return os.path.join(os.path.expanduser('~/.' + _posixify(app_name)))
|
||||
if sys.platform == 'darwin':
|
||||
return os.path.join(os.path.expanduser(
|
||||
'~/Library/Application Support'), app_name)
|
||||
return os.path.join(
|
||||
os.environ.get('XDG_CONFIG_HOME', os.path.expanduser('~/.config')),
|
||||
_posixify(app_name))
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user