py2ls 0.1.10.24__tar.gz → 0.1.10.26__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/PKG-INFO +7 -2
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/batman.py +134 -43
- py2ls-0.1.10.26/py2ls/data/email/email_html_template.html +88 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/ips.py +155 -34
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/pyproject.toml +2 -2
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/README.md +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.DS_Store +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/COMMIT_EDITMSG +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/FETCH_HEAD +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/HEAD +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/config +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/description +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/applypatch-msg.sample +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/commit-msg.sample +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/fsmonitor-watchman.sample +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/post-update.sample +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/pre-applypatch.sample +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/pre-commit.sample +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/pre-merge-commit.sample +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/pre-push.sample +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/pre-rebase.sample +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/pre-receive.sample +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/prepare-commit-msg.sample +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/push-to-checkout.sample +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/update.sample +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/index +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/info/exclude +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/logs/HEAD +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/logs/refs/heads/main +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/logs/refs/remotes/origin/HEAD +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/logs/refs/remotes/origin/main +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/01/d5bd8065e6860c0bd23ff9fa57161806a099e1 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/09/08da26de58c114225ad81f484b80bf5d351b34 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/0b/409e1bc918277010f5679b402d1d1dda53e15c +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/12/c2808a1b3a4d0892a4154dfba1e2ae3770fa73 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/14/449a0e6ba4ea2f1a73acf63ef91c9c6193f9ed +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/15/a8e468aacfcb440e090020f36d0b985d45da23 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/1a/b4585881a6a42889f01aa0cfe25fd5acfaf46f +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/1c/3f92adda34344bcbbbf9d409c79855ae2aaea8 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/1c/9314c5f69b9390068a2a8616875d974849d71f +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/1d/fe9d9633b24ea560354f4f93d39c6e5f163ea0 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/20/72c28e83f4347959d29f7b3a6c1fc3e4ee6b59 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/24/6b368b986f758630c46dc02b7fa512b53422f7 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/25/b796accd261b9135fd32a2c00785f68edf6c46 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/27/aa6074f652bc6f7078f8647489d9ee8e24f0e2 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/28/c2969d785c1b892c2a96b3f00eba63a59811b3 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/2a/ae95d517d213b660bf4f65a4e0cfae7bb893eb +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/2a/fdf45791a26d42ccead35ace76a8f0b2a56561 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/30/a2f8da47ee947811dc8d993f5a06a45de374f4 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/32/fd627b62fad7cf3b2f9e34ab9777126a0987ad +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/34/9e31b6a3634cea102ce5588b98c11cc1738605 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/34/b6f3a2ee84f39bed4eee57f2c0e0afb994feb1 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/35/1a5f491ab97eee9d1ee699478d75a8bb5d3dc2 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/36/b4a1b7403abc6c360f8fe2cb656ab945254971 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/36/e56a361f526eafa59c5235a5c990bf288b5f9c +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/36/ef43e50009e59db11812c258846d9e38718173 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/39/7ead045fbbcfb17c62019eb18fe21ed05dbee5 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/39/b13be65125556784e44c7a1d9821703c7ab67e +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/3b/507acc7f23391644cc0b824b1e79fd2677a362 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/3b/bd972aa7ad680858f8dfbd0f7fcd97756f0d6f +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/3c/bbe5f4173d165127b9ad96119f1ec24c306ffc +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/3d/9d10d27724657a436c65a6254bfd213d4b3562 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/3f/d6561300938afbb3d11976cf9c8f29549280d9 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/41/dcf4b3bf0460946b2da93776cf9e836d62178f +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/43/dbd49b2ee367c5434dd545e3b5795434f2ef0b +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/45/b1b6178bacbfc997811a998b5cc60c1ea7fac8 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/47/6cbd5a7c5e35cddef2f8a38bdc4896d403b095 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/48/a88fc5806305d0bb0755ee6801161b79696972 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/4f/7afb40dff2153d857fc85748c2eecb85125042 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/50/08ddfcf53c02e82d7eee2e57c38e5672ef89f6 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/53/e0deb1cb4c2c606bced6e7f9a66b0fda60980d +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/56/e4e8b2d5545e0256090f45aa8fc42c5fe067d0 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/57/63d0c52f5c9c69e89d514a1f96034947abe21a +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/57/bd1c0199483ab316235b094543b85edec6c35e +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/58/20a729045d4dc7e37ccaf8aa8eec126850afe2 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/59/380c4c26bdcd4d9b71ae3e2e35f05b3f26c5ab +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/5a/192565abf89c9d765af846ce6d53a92b1ce7ad +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/60/f273eb1c412d916fa3f11318a7da7a9911b52a +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/61/570cec8c061abe74121f27f5face6c69b98f99 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/62/4488173ed2c8936fa5cea3cf5dd3f26a30b86e +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/62/7c81b23b4e56e87b042b650b0103653cc9e34a +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/62/d90ccf8cbefdc2e4fd475e7c6f4f76e9fdf801 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/63/100154b27846e8010e55b6bf4b3d7762c14c5f +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/64/27a4edff08f93d98f511418423f09f2ab90bcd +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/66/6227eeeba24073e63811e89f1449f3d958f183 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/66/c998778721d424bd0aae80602dabbffa93af2e +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/68/6df3072c8b025fb18106ed2df505994ad062a9 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/69/13c452ca319f7cbf6a0836dc10a5bb033c84e4 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/6a/52e747a2b349b128d1490d9e896d2323818eb7 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/6b/7fde264d93a7a0986d394c46c7650d0ce2ab92 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/6c/cebb29b7f3f5b0c889f6dadbf9ff066554587d +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/6d/c2cdf4a84e538e5d4777486aeff87e42f41799 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/6d/ee29dbdcc84edeeacede105110446f3ccac963 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/71/36b2074a2754be8b58127d82250e5b37e3c373 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/72/245a05b0966011cb381e6b32b0465000e969ab +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/72/e4179337639859678ddaecf38b16f33aaec8e1 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/78/063f4c863fc371ec0313303c0a81283b35d9b6 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/78/3d4167bc95c9d2175e0df03ef1c1c880ba75ab +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/79/7ae089b2212a937840e215276005ce76881307 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/7e/5956c806b5edc344d46dab599dec337891ba1f +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/81/8f26b7bf042269729020cf944fc362d66ba27e +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/82/70b319ce4046854fbe7dc41054b6c2d112dab2 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/84/59071b722a255b774a80b27746033f8141ab39 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/85/aee46f478e9afdb84d50a05242c53b04ed2e21 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/86/e288b46f8fe179907e4413f665aeb5053fddb1 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/87/ef1fc3f7f1ddc4d0ab9b3e65381ce9f3388621 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/8b/84f56978e1de8f2ae82abce5f8b3e182d365cd +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/8e/55a7d2b96184030211f20c9b9af201eefcac82 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/91/c69ad88fe0ba94aa7859fb5f7edac5e6f1a3f7 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/94/74152b4b463d70ae5ad07f0c658be3e296026b +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/94/f7dbe88e80c4205a901b71eb8f181974376bba +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/97/1aef09ea939f46b60b9646f8d524c78a9220f4 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/9b/ec5ee2236ee2d5532c36bfd132e23c58fdb69c +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/9d/0df52899fe95279059286d9c0ec42287edc168 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/a1/5389729850729fc7bd78a54f26fce77f30be12 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/a1/906da89d1174f74867800c74c43af36253bd5e +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/a4/63fdd23e5efd713db8a71f316f3a1c7bd60916 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/a5/ec8f74642fbba27f7ea78c53b372ae0c7dedce +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/a7/3e13eafee65c5b8d73ad2d3ea46d0eee82f0d3 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/b0/56be4be89ba6b76949dd641df45bb7036050c8 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/b0/9cd7856d58590578ee1a4f3ad45d1310a97f87 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/b2/18e6a0f0f1c4df8cdefa9852058348abc713b7 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/b3/4f7f271c6d6105e35a6556ffda71d03afe8c96 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/b3/69579064bde9de9a19d114fc33e4e48cc8c0e4 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/b5/61831c7dce8ea51e7ee6b6fa35745f14d8242d +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/b7/2c9e75ab7d0afe594664650aa8f6c772f5ac64 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/bb/81ccc0513f18fc160b54a82861e9a80d23f4f6 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/bb/934eb33bc1a8b85630bf680caffd99560c1b8f +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/bf/67907e337021ebff434e02b19b30a741c144af +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/bf/b54d65922ce1dfda1aaa014913a54e7172d0bc +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/c1/20fc812b9ad311c34a3608512d6a9d976bb48e +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/c1/397c6ed72c4e20ef6b9ab83163e9a6baba5b45 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/c4/cba65f1163661999ee4b8ed23342b63bc1300c +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/c6/7f17e5707313600efcb85e9a3fedea35dba591 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/c6/f32aced880bd165a251cb52b26b0c1107e2141 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/cc/45df1d317a2eb63ff1ff3a5f3b4a9f98fd92b5 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/cd/822b3574a88ebdd1ed82fd6983f37e626d52b4 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/cf/0c0d9c6fb09473aaeb7f7e2edbd770c3f2ef3d +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/d2/992df305f4b56a466a2f221aeb182ddd20f418 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/d6/39e8af592cd75a318d8affddd1bcc70c2095f2 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/d6/9ab1c4aadf279936dd778e8346ba60f74705b6 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/d8/4688b54c0040a30976b3a6540bc47adf7ce680 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/d9/005f2cc7fc4e65f14ed5518276007c08cf2fd0 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/d9/c2403fd166ce791b4e9d0c6792ed8342c71fcd +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/d9/dfa5aee51e92a541b707e8e7baea6f06deff98 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/db/141dbaa93594df2a8156182f361ee4db829359 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/db/3f2cd643292057936230b95cf7ec3046affe11 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/db/ffa8ea7bda721d0cee7b9e4ce5b2ef927733ff +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/dc/c2bdbafb3296e09d9ee4955cfa55d275825f94 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/dc/cdbd4266765d840be2ae35ab1752a0fa312c16 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/dd/87fb5f606fe380d81e6fe3a2c98f9f99e3e09b +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/de/214c626ac2dd2685bfaa0bc0fc20f528d014d7 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/df/e0770424b2a19faf507a501ebfc23be8f54e7b +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/e2/f2f8f4c25e62a297fc55f36acc6b01cfbab76f +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/e3/1356f90ea6dd0577b5e0b40b206319adcbf085 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/e3/5a4dafc50850cacac7bf76c56db2715cbda2c4 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/e4/6c715352db9fe3c887a635f1916df4ca1f4ff9 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/e5/0580a0bd1e1b3d29f834382b80fceb61d5cf0c +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/e9/391ffe371f1cc43b42ef09b705d9c767c2e14f +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/ea/3a18cc75e53792744ef754e05d3f4481768c13 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/ec/40fd8bf8e4c342534a9fc020289e402ba6bc9d +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/ec/d980279432b13f0374b90ca439a6329cdece0f +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/ee/cee64eacaff022dcdc509c0c2b1da492f21060 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/f1/e50757fddc28b445545dc7e2759b54cdd0f42e +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/f4/b64d3107b39e3ad6f540c6607004ea34e6c024 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/f4/ba7f815b886797b73fede071d86e0c134d2bc7 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/f5/61c3c1bf1c9ea9c9d1f556a7be2869f71f3bdf +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/f6/44a8ff56fa035105fc517cbb1ac46c3d8e349a +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/f7/c98ba5c2f903e603b1f5e63d49fbc8a43815cc +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/f9/045a08e96eb76848fc4d68e3e3e687cca39a2d +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/fa/147e6bb78a2e8db241d231295fd7f1ed061af8 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/fc/292e793ecfd42240ac43be407023bd731fa9e7 +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/refs/heads/main +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/refs/remotes/origin/HEAD +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/refs/remotes/origin/main +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.gitattributes +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.gitignore +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/LICENSE +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/README.md +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/__init__.py +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/brain_atlas.py +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/chat.py +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/correlators.py +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/.DS_Store +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/db2ls_sql_chtsht.json +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/docs_links.json +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/lang_code_iso639.json +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/example/style1.pdf +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/example/style2.pdf +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/example/style3.pdf +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/example/style4.pdf +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/example/style5.pdf +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/example/style6.pdf +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/example/style7.pdf +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/example/style8.pdf +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/example/style9.pdf +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style1.json +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style10.json +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style11.json +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style12.json +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style2.json +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style3.json +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style4.json +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style5.json +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style6.json +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style7.json +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style8.json +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style9.json +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/db2ls.py +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/doc.py +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/export_requirements.py +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/freqanalysis.py +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/ich2ls.py +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/netfinder.py +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/ocr.py +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/plot.py +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/setuptools-70.1.0-py3-none-any.whl +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/sleep_events_detectors.py +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/stats.py +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/translator.py +0 -0
- {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/wb_detector.py +0 -0
@@ -1,11 +1,16 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: py2ls
|
3
|
-
Version: 0.1.10.
|
3
|
+
Version: 0.1.10.26
|
4
4
|
Summary: py(thon)2(too)ls
|
5
5
|
Author: Jianfeng
|
6
6
|
Author-email: Jianfeng.Liu0413@gmail.com
|
7
|
-
Requires-Python: >=3.
|
7
|
+
Requires-Python: >=3.5,<4.0
|
8
8
|
Classifier: Programming Language :: Python :: 3
|
9
|
+
Classifier: Programming Language :: Python :: 3.5
|
10
|
+
Classifier: Programming Language :: Python :: 3.6
|
11
|
+
Classifier: Programming Language :: Python :: 3.7
|
12
|
+
Classifier: Programming Language :: Python :: 3.8
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
9
14
|
Classifier: Programming Language :: Python :: 3.10
|
10
15
|
Classifier: Programming Language :: Python :: 3.11
|
11
16
|
Classifier: Programming Language :: Python :: 3.12
|
@@ -3,33 +3,75 @@ from email.mime.text import MIMEText
|
|
3
3
|
from email.mime.multipart import MIMEMultipart
|
4
4
|
from email.mime.base import MIMEBase
|
5
5
|
from email import encoders
|
6
|
-
import schedule
|
7
6
|
import time
|
8
7
|
from datetime import datetime
|
9
8
|
import os
|
10
9
|
from pprint import pp
|
11
10
|
import json
|
11
|
+
import mimetypes
|
12
|
+
import sys
|
12
13
|
|
13
14
|
|
14
|
-
|
15
|
-
def convert_to_html(text_list):
|
15
|
+
def convert_to_html(text_list, strict=False):
|
16
16
|
if not isinstance(text_list, list):
|
17
17
|
text_list = [text_list]
|
18
18
|
|
19
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
19
|
+
# Define a mapping for escape sequences to their HTML representations
|
20
|
+
escape_mapping = {
|
21
|
+
"\\": "\", # Backslash
|
22
|
+
"'": "'", # Single quote
|
23
|
+
'"': """, # Double quote
|
24
|
+
"\n": "<br>", # Newline
|
25
|
+
"\r": "", # Carriage return (not represented in HTML)
|
26
|
+
"\t": " ", # Tab (4 spaces)
|
27
|
+
"\b": "", # Backspace (not typically represented)
|
28
|
+
"\f": "", # Form feed (not typically represented)
|
29
|
+
"\v": "", # Vertical tab (not typically represented)
|
30
|
+
"\a": "", # Bell/alert sound (not typically represented)
|
31
|
+
"\0": "", # Null character (not typically represented)
|
32
|
+
}
|
33
|
+
# Convert text by replacing escape sequences with their HTML equivalents
|
34
|
+
html_content = ""
|
35
|
+
for text in text_list:
|
36
|
+
for escape_seq, html_rep in escape_mapping.items():
|
37
|
+
text = text.replace(escape_seq, html_rep)
|
38
|
+
html_content += text.replace("\n", "<br>") # Add line breaks for newlines
|
39
|
+
if strict:
|
40
|
+
html_content = "<html><body>\n" + html_content + "\n</body></html>"
|
26
41
|
return html_content
|
27
42
|
|
28
43
|
|
44
|
+
def get_name(email_str, by="@"):
|
45
|
+
if "num" in by:
|
46
|
+
for i, char in enumerate(email_str):
|
47
|
+
if char.isdigit():
|
48
|
+
break
|
49
|
+
else:
|
50
|
+
return email_str
|
51
|
+
return email_str[:i]
|
52
|
+
|
53
|
+
# Handle the case where '@' is the criteria
|
54
|
+
elif "@" in by:
|
55
|
+
name = email_str.split("@")[0]
|
56
|
+
name = get_name(name, by="num")
|
57
|
+
name = " ".join([part.capitalize() for part in name.split(".")])
|
58
|
+
return name
|
59
|
+
|
60
|
+
|
29
61
|
def extract_kv(
|
30
|
-
dir_data=
|
62
|
+
dir_data=None,
|
31
63
|
idx=0,
|
32
64
|
): # select the index
|
65
|
+
if dir_data is None:
|
66
|
+
if "dar" in sys.platform:
|
67
|
+
dir_data = "/Users/macjianfeng/Dropbox/github/python/py2ls/confidential_data/gmail_login.json"
|
68
|
+
else:
|
69
|
+
if "win" in sys.platform:
|
70
|
+
dir_data = (
|
71
|
+
"Z:\\Jianfeng\\Apps\\settings\\confidential_data\\gmail_login.json"
|
72
|
+
)
|
73
|
+
elif "lin" in sys.platform:
|
74
|
+
dir_data = "/Users/macjianfeng/Dropbox/github/python/py2ls/confidential_data/gmail_login.json"
|
33
75
|
with open(dir_data, "r") as file:
|
34
76
|
email_login = json.load(file)
|
35
77
|
for i, (user, pd) in enumerate(email_login.items()):
|
@@ -38,25 +80,36 @@ def extract_kv(
|
|
38
80
|
|
39
81
|
|
40
82
|
def email_to(**kwargs):
|
83
|
+
return email(**kwargs)
|
84
|
+
|
85
|
+
|
86
|
+
def email(**kwargs):
|
41
87
|
"""
|
42
88
|
usage:
|
43
|
-
email_to(who="example@gmail.com",
|
44
|
-
|
45
|
-
|
46
|
-
|
89
|
+
email_to(who=["example@gmail.com"],
|
90
|
+
subject="test",
|
91
|
+
what="this is the body",
|
92
|
+
attachments=["/Users/test.pdf"],
|
93
|
+
cc=["cc1@example.com"],
|
94
|
+
bcc=["bcc@example.com"],
|
95
|
+
html_template="template.html")
|
47
96
|
Send an email with optional attachments.
|
48
97
|
|
49
|
-
:param who: Recipient email address
|
98
|
+
:param who: Recipient email address (string or list)
|
50
99
|
:param subject: Email subject
|
51
|
-
:param what: Email
|
100
|
+
:param what: Email body (can be HTML or plain text)
|
52
101
|
:param attachments: List of file paths to attach
|
102
|
+
:param cc: List of CC email addresses
|
103
|
+
:param bcc: List of BCC email addresses, "blind carbon copy"接受者之间互相看不到
|
104
|
+
:param html_template: Path to an HTML file for the email body
|
53
105
|
"""
|
54
106
|
who, what, subject, signature = None, None, None, None
|
55
107
|
attachments = False
|
56
108
|
pause_sec = False # default 10 seconds pause
|
57
|
-
|
109
|
+
bcc, cc = [], []
|
58
110
|
signature_styles = extract_kv(idx=1)[1] # signature list
|
59
111
|
signature = signature_styles[0] # default signature,None
|
112
|
+
read_receipt = False
|
60
113
|
verbose = True
|
61
114
|
if not kwargs:
|
62
115
|
print(
|
@@ -64,49 +117,73 @@ def email_to(**kwargs):
|
|
64
117
|
)
|
65
118
|
return None
|
66
119
|
# params config
|
120
|
+
for k, v in kwargs.items():
|
121
|
+
if any([i in k.lower() for i in ["who", "whe", "to", "@"]]): # 'who' or "where"
|
122
|
+
if isinstance(v, list):
|
123
|
+
who = v
|
124
|
+
else:
|
125
|
+
who = [v]
|
67
126
|
for k, v in kwargs.items():
|
68
127
|
if any(
|
69
|
-
[
|
70
|
-
|
71
|
-
"whe" in k.lower(),
|
72
|
-
"to" in k.lower(),
|
73
|
-
"@" in k.lower(),
|
74
|
-
all(["@" in k.lower(), "." in k.lower()]),
|
75
|
-
]
|
76
|
-
): # 'who' or "where"
|
77
|
-
who = v
|
78
|
-
if any(["sub" in k.lower(), "hea" in k.lower()]): # 'subject', 'header'
|
128
|
+
[i in k.lower() for i in ["sub", "hea", "top"]]
|
129
|
+
): # 'subject', 'header','topic'
|
79
130
|
subject = v
|
80
131
|
if any(
|
81
|
-
[
|
132
|
+
[i in k.lower() for i in ["wha", "con", "bod"]]
|
82
133
|
): # 'what','content','body'
|
83
134
|
what = v
|
84
|
-
if any([
|
135
|
+
if any([i in k.lower() for i in ["att", "fil"]]):
|
85
136
|
attachments = v # optional
|
86
|
-
if any(
|
87
|
-
["paus" in k.lower(), "tim" in k.lower(), "delay" in k.lower()]
|
88
|
-
): # 'attachments', 'file'
|
137
|
+
if any([i in k.lower() for i in ["paus", "tim", "delay"]]):
|
89
138
|
pause_sec = v # optional
|
90
139
|
if any(["sig" in k.lower()]): # 'attachments', 'file'
|
91
140
|
signature = v # optional
|
92
141
|
if not isinstance(v, str):
|
93
|
-
print(signature_styles)
|
94
142
|
signature = signature_styles[v]
|
95
|
-
if any([
|
143
|
+
if any([i in k.lower() for i in ["verb", "show"]]): # 'verbose', 'show'
|
96
144
|
verbose = v # verbose
|
97
|
-
|
145
|
+
if any(["cc" in k.lower() and "bcc" not in k.lower(), "ward" in k.lower()]):
|
146
|
+
if isinstance(v, list):
|
147
|
+
cc = v
|
148
|
+
else:
|
149
|
+
cc = [v]
|
150
|
+
if any([i in k.lower() for i in ["bcc", "blind", "secret"]]):
|
151
|
+
if isinstance(v, list):
|
152
|
+
bcc = v
|
153
|
+
else:
|
154
|
+
bcc = [v]
|
155
|
+
if any(["temp" in k.lower(), "html" in k.lower()]):
|
156
|
+
# Load HTML template if specified
|
157
|
+
if isinstance(v, str) and v.endswith("html"):
|
158
|
+
with open(kwargs["html_template"], "r") as f:
|
159
|
+
what = f.read()
|
98
160
|
# Create email message
|
99
161
|
message = MIMEMultipart()
|
100
162
|
message["From"] = extract_kv()[0]
|
101
|
-
|
163
|
+
print(who)
|
164
|
+
message["To"] = ", ".join(who)
|
165
|
+
|
166
|
+
if len(who) == 1:
|
167
|
+
who = ", ".join(who)
|
168
|
+
|
102
169
|
message["Subject"] = subject
|
170
|
+
# Add CC addresses if provided
|
171
|
+
if cc:
|
172
|
+
message["Cc"] = ", ".join(cc) # Join CC addresses as a comma-separated string
|
103
173
|
|
104
174
|
# the eamil's body
|
105
175
|
if what is None:
|
106
176
|
what = ""
|
107
177
|
if isinstance(what, list):
|
108
178
|
what = convert_to_html(what)
|
109
|
-
if 'class="
|
179
|
+
if 'class="' in str(what):
|
180
|
+
# Combine HTML content correctly
|
181
|
+
html_content = "<html><body>\n"
|
182
|
+
html_content += f"{what}"
|
183
|
+
html_content += f"<br><br>{convert_to_html([signature])}"
|
184
|
+
html_content += "\n</body></html>"
|
185
|
+
message.attach(MIMEText(html_content, "html"))
|
186
|
+
elif 'class="' in str(attachments):
|
110
187
|
# Combine HTML content correctly
|
111
188
|
html_content = "<html><body>\n"
|
112
189
|
html_content += f"{convert_to_html(what)}<br>{attachments}"
|
@@ -124,15 +201,24 @@ def email_to(**kwargs):
|
|
124
201
|
if isinstance(attachments, str):
|
125
202
|
attachments = [attachments]
|
126
203
|
for file in attachments:
|
127
|
-
|
204
|
+
# 有时候一些不常用的文件类型, 无法自动识别
|
205
|
+
mime_type, _ = mimetypes.guess_type(file)
|
206
|
+
if mime_type is None:
|
207
|
+
if file.endswith(".xlsx"):
|
208
|
+
mime_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
209
|
+
else:
|
210
|
+
mime_type = "application/octet-stream"
|
211
|
+
|
212
|
+
main_type, sub_type = mime_type.split("/", 1)
|
213
|
+
# part = MIMEBase("application", "octet-stream") # 旧的版本
|
214
|
+
part = MIMEBase(main_type, sub_type)
|
128
215
|
try:
|
129
216
|
with open(file, "rb") as attachment:
|
130
217
|
part.set_payload(attachment.read())
|
131
218
|
encoders.encode_base64(part)
|
132
|
-
|
219
|
+
filename = os.path.basename(file)
|
133
220
|
part.add_header(
|
134
|
-
"Content-Disposition",
|
135
|
-
f'attachment; filename= "{os.path.basename(file)}"',
|
221
|
+
"Content-Disposition", f'attachment; filename="{filename}"'
|
136
222
|
)
|
137
223
|
message.attach(part)
|
138
224
|
except FileNotFoundError:
|
@@ -147,7 +233,12 @@ def email_to(**kwargs):
|
|
147
233
|
with smtplib.SMTP("smtp.gmail.com", 587) as server:
|
148
234
|
server.starttls()
|
149
235
|
server.login(extract_kv()[0], extract_kv()[1])
|
150
|
-
|
236
|
+
if any(bcc):
|
237
|
+
recipient_list = who + cc + bcc # Include all recipients
|
238
|
+
server.sendmail(extract_kv()[0], recipient_list, message.as_string())
|
239
|
+
else:
|
240
|
+
server.sendmail(extract_kv()[0], who, message.as_string())
|
241
|
+
|
151
242
|
if verbose:
|
152
243
|
if attachments:
|
153
244
|
print(
|
@@ -0,0 +1,88 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<title>Email Template</title>
|
7
|
+
<style>
|
8
|
+
body {
|
9
|
+
font-family: Arial, sans-serif;
|
10
|
+
margin: 0;
|
11
|
+
padding: 0;
|
12
|
+
background-color: #f4f4f4;
|
13
|
+
}
|
14
|
+
.email-container {
|
15
|
+
max-width: 600px;
|
16
|
+
margin: 0 auto;
|
17
|
+
background-color: #ffffff;
|
18
|
+
padding: 20px;
|
19
|
+
border-radius: 8px;
|
20
|
+
box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1);
|
21
|
+
}
|
22
|
+
.email-header {
|
23
|
+
background-color: #4CAF50;
|
24
|
+
color: #ffffff;
|
25
|
+
padding: 10px;
|
26
|
+
text-align: center;
|
27
|
+
border-radius: 8px 8px 0 0;
|
28
|
+
}
|
29
|
+
.email-body {
|
30
|
+
padding: 20px;
|
31
|
+
font-size: 16px;
|
32
|
+
color: #333333;
|
33
|
+
line-height: 1.5;
|
34
|
+
}
|
35
|
+
.email-footer {
|
36
|
+
background-color: #f4f4f4;
|
37
|
+
color: #888888;
|
38
|
+
padding: 10px;
|
39
|
+
text-align: center;
|
40
|
+
font-size: 12px;
|
41
|
+
border-radius: 0 0 8px 8px;
|
42
|
+
}
|
43
|
+
a {
|
44
|
+
color: #4CAF50;
|
45
|
+
text-decoration: none;
|
46
|
+
}
|
47
|
+
a:hover {
|
48
|
+
text-decoration: underline;
|
49
|
+
}
|
50
|
+
.button {
|
51
|
+
background-color: #4CAF50;
|
52
|
+
color: #ffffff;
|
53
|
+
padding: 10px 20px;
|
54
|
+
text-align: center;
|
55
|
+
display: inline-block;
|
56
|
+
margin: 10px 0;
|
57
|
+
border-radius: 4px;
|
58
|
+
text-decoration: none;
|
59
|
+
font-size: 16px;
|
60
|
+
}
|
61
|
+
.button:hover {
|
62
|
+
background-color: #45a049;
|
63
|
+
}
|
64
|
+
</style>
|
65
|
+
</head>
|
66
|
+
<body>
|
67
|
+
<div class="email-container">
|
68
|
+
<div class="email-header">
|
69
|
+
<h1>Welcome to Our Newsletter</h1>
|
70
|
+
</div>
|
71
|
+
<div class="email-body">
|
72
|
+
<p>Dear Subscriber,</p>
|
73
|
+
<p>Thank you for signing up for our newsletter! We are excited to keep you updated with the latest news, promotions, and events. Here’s what you can expect in our upcoming editions:</p>
|
74
|
+
<ul>
|
75
|
+
<li>Exclusive promotions and discounts</li>
|
76
|
+
<li>Updates on our latest products</li>
|
77
|
+
<li>Invitations to special events</li>
|
78
|
+
</ul>
|
79
|
+
<p>Stay tuned for more exciting updates. In the meantime, feel free to visit our website for more information!</p>
|
80
|
+
<a href="https://example.com" class="button">Visit Our Website</a>
|
81
|
+
</div>
|
82
|
+
<div class="email-footer">
|
83
|
+
<p>© 2024 Company Name. All rights reserved.</p>
|
84
|
+
<p>If you no longer wish to receive these emails, please <a href="#">unsubscribe</a>.</p>
|
85
|
+
</div>
|
86
|
+
</div>
|
87
|
+
</body>
|
88
|
+
</html>
|
@@ -47,6 +47,8 @@ from fuzzywuzzy import fuzz, process
|
|
47
47
|
from langdetect import detect
|
48
48
|
from duckduckgo_search import DDGS
|
49
49
|
|
50
|
+
from bs4 import BeautifulSoup
|
51
|
+
|
50
52
|
from . import netfinder
|
51
53
|
|
52
54
|
# from .plot import get_color
|
@@ -58,6 +60,16 @@ except NameError:
|
|
58
60
|
pass
|
59
61
|
|
60
62
|
|
63
|
+
# set 'dir_save'
|
64
|
+
if "dar" in sys.platform:
|
65
|
+
dir_save = "/Users/macjianfeng/Dropbox/Downloads/"
|
66
|
+
else:
|
67
|
+
if "win" in sys.platform:
|
68
|
+
dir_save = "Z:\\Jianfeng\\temp\\"
|
69
|
+
elif "lin" in sys.platform:
|
70
|
+
dir_data = "/Users/macjianfeng/Dropbox/github/python/py2ls/confidential_data/gmail_login.json"
|
71
|
+
|
72
|
+
|
61
73
|
def unique(lst, ascending=None):
|
62
74
|
"""
|
63
75
|
移除列表中的重复元素,同时可以选择按升序或降序排序。
|
@@ -261,9 +273,6 @@ def upgrade(module="py2ls"):
|
|
261
273
|
print(f"An error occurred while upgrading py2ls: {e}")
|
262
274
|
|
263
275
|
|
264
|
-
dir_save = "/Users/macjianfeng/Dropbox/Downloads/"
|
265
|
-
|
266
|
-
|
267
276
|
def get_version(pkg):
|
268
277
|
import importlib.metadata
|
269
278
|
|
@@ -749,6 +758,41 @@ def num2str(num, *args, **kwargs):
|
|
749
758
|
# num2str(12345333.6789, sep=","), type(num2str(12345.6789, ","))
|
750
759
|
# ) # Output: "12,345.6789"
|
751
760
|
# print(num2str(7000.00, sep=","), type(num2str(7000.00, ","))) # Output: "7,000.00"
|
761
|
+
|
762
|
+
|
763
|
+
# Helper to convert text or list of text to HTML
|
764
|
+
def str2html(text_list, strict=False):
|
765
|
+
if not isinstance(text_list, list):
|
766
|
+
text_list = [text_list]
|
767
|
+
|
768
|
+
# Define a mapping for escape sequences to their HTML representations
|
769
|
+
escape_mapping = {
|
770
|
+
"\\": "\", # Backslash
|
771
|
+
"'": "'", # Single quote
|
772
|
+
'"': """, # Double quote
|
773
|
+
"\n": "<br>", # Newline
|
774
|
+
"\r": "", # Carriage return (not represented in HTML)
|
775
|
+
"\t": " ", # Tab (4 spaces)
|
776
|
+
"\b": "", # Backspace (not typically represented)
|
777
|
+
"\f": "", # Form feed (not typically represented)
|
778
|
+
"\v": "", # Vertical tab (not typically represented)
|
779
|
+
"\a": "", # Bell/alert sound (not typically represented)
|
780
|
+
"\0": "", # Null character (not typically represented)
|
781
|
+
}
|
782
|
+
|
783
|
+
# Convert text by replacing escape sequences with their HTML equivalents
|
784
|
+
html_content = ""
|
785
|
+
for text in text_list:
|
786
|
+
for escape_seq, html_rep in escape_mapping.items():
|
787
|
+
text = text.replace(escape_seq, html_rep)
|
788
|
+
html_content += text.replace("\n", "<br>") # Add line breaks for newlines
|
789
|
+
|
790
|
+
if strict:
|
791
|
+
html_content = "<html><body>\n" + html_content + "\n</body></html>"
|
792
|
+
|
793
|
+
return html_content
|
794
|
+
|
795
|
+
|
752
796
|
def sreplace(*args, **kwargs):
|
753
797
|
"""
|
754
798
|
sreplace(text, by=None, robust=True)
|
@@ -1770,46 +1814,72 @@ def sort_kind(df, by="name", ascending=True):
|
|
1770
1814
|
return sorted_df
|
1771
1815
|
|
1772
1816
|
|
1773
|
-
def isa(
|
1817
|
+
def isa(content, kind):
|
1774
1818
|
"""
|
1775
|
-
|
1776
|
-
|
1819
|
+
content, kind='img'
|
1820
|
+
kinds file paths based on the specified kind.
|
1777
1821
|
Args:
|
1778
|
-
|
1779
|
-
|
1822
|
+
content (str): Path to the file.
|
1823
|
+
kind (str): kind of file to kind. Default is 'img' for images. Other options include 'doc' for documents,
|
1780
1824
|
'zip' for ZIP archives, and 'other' for other types of files.
|
1781
1825
|
Returns:
|
1782
|
-
bool: True if the file matches the
|
1826
|
+
bool: True if the file matches the kind, False otherwise.
|
1783
1827
|
"""
|
1784
|
-
|
1785
|
-
|
1786
|
-
|
1787
|
-
|
1788
|
-
|
1789
|
-
|
1790
|
-
|
1791
|
-
return
|
1792
|
-
elif "
|
1793
|
-
return
|
1794
|
-
elif "
|
1795
|
-
return
|
1796
|
-
elif "
|
1797
|
-
|
1798
|
-
):
|
1799
|
-
return
|
1800
|
-
elif "
|
1801
|
-
|
1802
|
-
|
1803
|
-
|
1804
|
-
|
1805
|
-
|
1806
|
-
|
1807
|
-
|
1828
|
+
if "img" in kind.lower() or "image" in kind.lower():
|
1829
|
+
return is_image(content)
|
1830
|
+
elif "doc" in kind.lower():
|
1831
|
+
return is_document(content)
|
1832
|
+
elif "zip" in kind.lower():
|
1833
|
+
return is_zip(content)
|
1834
|
+
elif "dir" in kind.lower() or ("f" in kind.lower() and "d" in kind.lower()):
|
1835
|
+
return os.path.isdir(content)
|
1836
|
+
elif "fi" in kind.lower(): # file
|
1837
|
+
return os.path.isfile(content)
|
1838
|
+
elif "num" in kind.lower(): # file
|
1839
|
+
return os.path.isfile(content)
|
1840
|
+
elif "text" in kind.lower() or "txt" in kind.lower(): # file
|
1841
|
+
return is_text(content)
|
1842
|
+
elif "color" in kind.lower(): # file
|
1843
|
+
return is_str_color(content)
|
1844
|
+
elif "html" in kind.lower():
|
1845
|
+
if content is None or not isinstance(content, str):
|
1846
|
+
return False
|
1847
|
+
# Remove leading and trailing whitespace
|
1848
|
+
content = content.strip()
|
1849
|
+
# Check for common HTML tags using regex
|
1850
|
+
# This pattern matches anything that looks like an HTML tag
|
1851
|
+
tag_pattern = r"<[a-zA-Z][^>]*>(.*?)</[a-zA-Z][^>]*>"
|
1852
|
+
# Check for any opening and closing tags
|
1853
|
+
if re.search(tag_pattern, content):
|
1854
|
+
return True
|
1855
|
+
# Additional checks for self-closing tags
|
1856
|
+
self_closing_tags = ["img", "br", "hr", "input", "meta", "link"]
|
1857
|
+
for tag in self_closing_tags:
|
1858
|
+
if f"<{tag}" in content:
|
1859
|
+
return True
|
1860
|
+
return False
|
1808
1861
|
else:
|
1809
|
-
print(f"{
|
1862
|
+
print(f"{kind} was not set up correctly")
|
1810
1863
|
return False
|
1811
1864
|
|
1812
1865
|
|
1866
|
+
import sys
|
1867
|
+
|
1868
|
+
|
1869
|
+
def get_os():
|
1870
|
+
os_name = sys.platform
|
1871
|
+
if "dar" in os_name:
|
1872
|
+
return "macOS"
|
1873
|
+
else:
|
1874
|
+
if "win" in os_name:
|
1875
|
+
return "Windows"
|
1876
|
+
elif "linux" in os_name:
|
1877
|
+
return "Linux"
|
1878
|
+
else:
|
1879
|
+
print(f"{os_name}, returned 'None'")
|
1880
|
+
return None
|
1881
|
+
|
1882
|
+
|
1813
1883
|
def listdir(
|
1814
1884
|
rootdir,
|
1815
1885
|
kind="folder",
|
@@ -3925,3 +3995,54 @@ format_excel(
|
|
3925
3995
|
# Save the workbook
|
3926
3996
|
wb.save(filename)
|
3927
3997
|
print(f"Formatted Excel file saved as:\n{filename}")
|
3998
|
+
|
3999
|
+
|
4000
|
+
from IPython.display import display, HTML, Markdown, Image
|
4001
|
+
|
4002
|
+
|
4003
|
+
def preview(var):
|
4004
|
+
"""Master function to preview formatted variables in Jupyter."""
|
4005
|
+
|
4006
|
+
if isinstance(var, str):
|
4007
|
+
if isa(var, "html"):
|
4008
|
+
display(HTML(var)) # Render as HTML
|
4009
|
+
# Check if it's a valid markdown
|
4010
|
+
elif var.startswith("#"):
|
4011
|
+
display(Markdown(var))
|
4012
|
+
else:
|
4013
|
+
# Otherwise, display as plain text
|
4014
|
+
print(var)
|
4015
|
+
elif isinstance(var, BeautifulSoup):
|
4016
|
+
preview(str(var))
|
4017
|
+
elif isinstance(var, pd.DataFrame):
|
4018
|
+
# Display pandas DataFrame
|
4019
|
+
display(var)
|
4020
|
+
|
4021
|
+
elif isinstance(var, list) or isinstance(var, dict):
|
4022
|
+
# Display JSON
|
4023
|
+
json_str = json.dumps(var, indent=4)
|
4024
|
+
display(Markdown(f"```json\n{json_str}\n```"))
|
4025
|
+
|
4026
|
+
elif isinstance(var, bytes):
|
4027
|
+
# Display image if it's in bytes format
|
4028
|
+
display(Image(data=var))
|
4029
|
+
|
4030
|
+
elif isinstance(var, str) and (var.endswith(".png") or var.endswith(".jpg")):
|
4031
|
+
# Display image from file path
|
4032
|
+
display(Image(filename=var))
|
4033
|
+
|
4034
|
+
elif isinstance(var, dict):
|
4035
|
+
# Handle dictionary formatting
|
4036
|
+
json_str = json.dumps(var, indent=4)
|
4037
|
+
display(Markdown(f"```json\n{json_str}\n```"))
|
4038
|
+
|
4039
|
+
else:
|
4040
|
+
# If the format is not recognized, print a message
|
4041
|
+
print("Format not recognized or unsupported.")
|
4042
|
+
|
4043
|
+
|
4044
|
+
# # Example usages:
|
4045
|
+
# preview("This is a plain text message.")
|
4046
|
+
# preview("# This is a Markdown header")
|
4047
|
+
# preview(pd.DataFrame({"Name": ["Alice", "Bob"], "Age": [25, 30]}))
|
4048
|
+
# preview({"key": "value", "numbers": [1, 2, 3]})
|
@@ -1,12 +1,12 @@
|
|
1
1
|
[tool.poetry]
|
2
2
|
name = "py2ls"
|
3
|
-
version = "0.1.10.
|
3
|
+
version = "0.1.10.26"
|
4
4
|
description = "py(thon)2(too)ls"
|
5
5
|
authors = ["Jianfeng <Jianfeng.Liu0413@gmail.com>"]
|
6
6
|
readme = "README.md"
|
7
7
|
|
8
8
|
[tool.poetry.dependencies]
|
9
|
-
python = "^3.
|
9
|
+
python = "^3.5"
|
10
10
|
#appnope = { version = ">=0.1.4", markers = "sys_platform == 'darwin'" }
|
11
11
|
#appscript = { version = ">=1.2.5", markers = "sys_platform == 'darwin'" }
|
12
12
|
asciitree = ">=0.3.3"
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|