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.
Files changed (221) hide show
  1. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/PKG-INFO +7 -2
  2. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/batman.py +134 -43
  3. py2ls-0.1.10.26/py2ls/data/email/email_html_template.html +88 -0
  4. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/ips.py +155 -34
  5. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/pyproject.toml +2 -2
  6. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/README.md +0 -0
  7. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.DS_Store +0 -0
  8. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/COMMIT_EDITMSG +0 -0
  9. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/FETCH_HEAD +0 -0
  10. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/HEAD +0 -0
  11. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/config +0 -0
  12. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/description +0 -0
  13. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/applypatch-msg.sample +0 -0
  14. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/commit-msg.sample +0 -0
  15. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/fsmonitor-watchman.sample +0 -0
  16. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/post-update.sample +0 -0
  17. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/pre-applypatch.sample +0 -0
  18. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/pre-commit.sample +0 -0
  19. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/pre-merge-commit.sample +0 -0
  20. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/pre-push.sample +0 -0
  21. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/pre-rebase.sample +0 -0
  22. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/pre-receive.sample +0 -0
  23. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/prepare-commit-msg.sample +0 -0
  24. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/push-to-checkout.sample +0 -0
  25. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/hooks/update.sample +0 -0
  26. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/index +0 -0
  27. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/info/exclude +0 -0
  28. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/logs/HEAD +0 -0
  29. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/logs/refs/heads/main +0 -0
  30. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/logs/refs/remotes/origin/HEAD +0 -0
  31. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/logs/refs/remotes/origin/main +0 -0
  32. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/01/d5bd8065e6860c0bd23ff9fa57161806a099e1 +0 -0
  33. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/09/08da26de58c114225ad81f484b80bf5d351b34 +0 -0
  34. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/0b/409e1bc918277010f5679b402d1d1dda53e15c +0 -0
  35. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/12/c2808a1b3a4d0892a4154dfba1e2ae3770fa73 +0 -0
  36. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/14/449a0e6ba4ea2f1a73acf63ef91c9c6193f9ed +0 -0
  37. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/15/a8e468aacfcb440e090020f36d0b985d45da23 +0 -0
  38. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/1a/b4585881a6a42889f01aa0cfe25fd5acfaf46f +0 -0
  39. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/1c/3f92adda34344bcbbbf9d409c79855ae2aaea8 +0 -0
  40. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/1c/9314c5f69b9390068a2a8616875d974849d71f +0 -0
  41. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/1d/fe9d9633b24ea560354f4f93d39c6e5f163ea0 +0 -0
  42. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/20/72c28e83f4347959d29f7b3a6c1fc3e4ee6b59 +0 -0
  43. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/24/6b368b986f758630c46dc02b7fa512b53422f7 +0 -0
  44. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/25/b796accd261b9135fd32a2c00785f68edf6c46 +0 -0
  45. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/27/aa6074f652bc6f7078f8647489d9ee8e24f0e2 +0 -0
  46. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/28/c2969d785c1b892c2a96b3f00eba63a59811b3 +0 -0
  47. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/2a/ae95d517d213b660bf4f65a4e0cfae7bb893eb +0 -0
  48. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/2a/fdf45791a26d42ccead35ace76a8f0b2a56561 +0 -0
  49. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/30/a2f8da47ee947811dc8d993f5a06a45de374f4 +0 -0
  50. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/32/fd627b62fad7cf3b2f9e34ab9777126a0987ad +0 -0
  51. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/34/9e31b6a3634cea102ce5588b98c11cc1738605 +0 -0
  52. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/34/b6f3a2ee84f39bed4eee57f2c0e0afb994feb1 +0 -0
  53. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/35/1a5f491ab97eee9d1ee699478d75a8bb5d3dc2 +0 -0
  54. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/36/b4a1b7403abc6c360f8fe2cb656ab945254971 +0 -0
  55. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/36/e56a361f526eafa59c5235a5c990bf288b5f9c +0 -0
  56. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/36/ef43e50009e59db11812c258846d9e38718173 +0 -0
  57. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/39/7ead045fbbcfb17c62019eb18fe21ed05dbee5 +0 -0
  58. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/39/b13be65125556784e44c7a1d9821703c7ab67e +0 -0
  59. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/3b/507acc7f23391644cc0b824b1e79fd2677a362 +0 -0
  60. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/3b/bd972aa7ad680858f8dfbd0f7fcd97756f0d6f +0 -0
  61. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/3c/bbe5f4173d165127b9ad96119f1ec24c306ffc +0 -0
  62. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/3d/9d10d27724657a436c65a6254bfd213d4b3562 +0 -0
  63. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/3f/d6561300938afbb3d11976cf9c8f29549280d9 +0 -0
  64. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/41/dcf4b3bf0460946b2da93776cf9e836d62178f +0 -0
  65. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/43/dbd49b2ee367c5434dd545e3b5795434f2ef0b +0 -0
  66. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/45/b1b6178bacbfc997811a998b5cc60c1ea7fac8 +0 -0
  67. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/47/6cbd5a7c5e35cddef2f8a38bdc4896d403b095 +0 -0
  68. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/48/a88fc5806305d0bb0755ee6801161b79696972 +0 -0
  69. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/4f/7afb40dff2153d857fc85748c2eecb85125042 +0 -0
  70. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/50/08ddfcf53c02e82d7eee2e57c38e5672ef89f6 +0 -0
  71. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/53/e0deb1cb4c2c606bced6e7f9a66b0fda60980d +0 -0
  72. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/56/e4e8b2d5545e0256090f45aa8fc42c5fe067d0 +0 -0
  73. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/57/63d0c52f5c9c69e89d514a1f96034947abe21a +0 -0
  74. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/57/bd1c0199483ab316235b094543b85edec6c35e +0 -0
  75. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/58/20a729045d4dc7e37ccaf8aa8eec126850afe2 +0 -0
  76. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/59/380c4c26bdcd4d9b71ae3e2e35f05b3f26c5ab +0 -0
  77. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/5a/192565abf89c9d765af846ce6d53a92b1ce7ad +0 -0
  78. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/60/f273eb1c412d916fa3f11318a7da7a9911b52a +0 -0
  79. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/61/570cec8c061abe74121f27f5face6c69b98f99 +0 -0
  80. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/62/4488173ed2c8936fa5cea3cf5dd3f26a30b86e +0 -0
  81. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/62/7c81b23b4e56e87b042b650b0103653cc9e34a +0 -0
  82. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/62/d90ccf8cbefdc2e4fd475e7c6f4f76e9fdf801 +0 -0
  83. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/63/100154b27846e8010e55b6bf4b3d7762c14c5f +0 -0
  84. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/64/27a4edff08f93d98f511418423f09f2ab90bcd +0 -0
  85. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/66/6227eeeba24073e63811e89f1449f3d958f183 +0 -0
  86. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/66/c998778721d424bd0aae80602dabbffa93af2e +0 -0
  87. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/68/6df3072c8b025fb18106ed2df505994ad062a9 +0 -0
  88. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/69/13c452ca319f7cbf6a0836dc10a5bb033c84e4 +0 -0
  89. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/6a/52e747a2b349b128d1490d9e896d2323818eb7 +0 -0
  90. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/6b/7fde264d93a7a0986d394c46c7650d0ce2ab92 +0 -0
  91. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/6c/cebb29b7f3f5b0c889f6dadbf9ff066554587d +0 -0
  92. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/6d/c2cdf4a84e538e5d4777486aeff87e42f41799 +0 -0
  93. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/6d/ee29dbdcc84edeeacede105110446f3ccac963 +0 -0
  94. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/71/36b2074a2754be8b58127d82250e5b37e3c373 +0 -0
  95. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/72/245a05b0966011cb381e6b32b0465000e969ab +0 -0
  96. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/72/e4179337639859678ddaecf38b16f33aaec8e1 +0 -0
  97. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/78/063f4c863fc371ec0313303c0a81283b35d9b6 +0 -0
  98. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/78/3d4167bc95c9d2175e0df03ef1c1c880ba75ab +0 -0
  99. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/79/7ae089b2212a937840e215276005ce76881307 +0 -0
  100. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/7e/5956c806b5edc344d46dab599dec337891ba1f +0 -0
  101. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/81/8f26b7bf042269729020cf944fc362d66ba27e +0 -0
  102. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/82/70b319ce4046854fbe7dc41054b6c2d112dab2 +0 -0
  103. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/84/59071b722a255b774a80b27746033f8141ab39 +0 -0
  104. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/85/aee46f478e9afdb84d50a05242c53b04ed2e21 +0 -0
  105. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/86/e288b46f8fe179907e4413f665aeb5053fddb1 +0 -0
  106. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/87/ef1fc3f7f1ddc4d0ab9b3e65381ce9f3388621 +0 -0
  107. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/8b/84f56978e1de8f2ae82abce5f8b3e182d365cd +0 -0
  108. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/8e/55a7d2b96184030211f20c9b9af201eefcac82 +0 -0
  109. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/91/c69ad88fe0ba94aa7859fb5f7edac5e6f1a3f7 +0 -0
  110. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/94/74152b4b463d70ae5ad07f0c658be3e296026b +0 -0
  111. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/94/f7dbe88e80c4205a901b71eb8f181974376bba +0 -0
  112. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/97/1aef09ea939f46b60b9646f8d524c78a9220f4 +0 -0
  113. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/9b/ec5ee2236ee2d5532c36bfd132e23c58fdb69c +0 -0
  114. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/9d/0df52899fe95279059286d9c0ec42287edc168 +0 -0
  115. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/a1/5389729850729fc7bd78a54f26fce77f30be12 +0 -0
  116. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/a1/906da89d1174f74867800c74c43af36253bd5e +0 -0
  117. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/a4/63fdd23e5efd713db8a71f316f3a1c7bd60916 +0 -0
  118. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/a5/ec8f74642fbba27f7ea78c53b372ae0c7dedce +0 -0
  119. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/a7/3e13eafee65c5b8d73ad2d3ea46d0eee82f0d3 +0 -0
  120. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/b0/56be4be89ba6b76949dd641df45bb7036050c8 +0 -0
  121. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/b0/9cd7856d58590578ee1a4f3ad45d1310a97f87 +0 -0
  122. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/b2/18e6a0f0f1c4df8cdefa9852058348abc713b7 +0 -0
  123. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/b3/4f7f271c6d6105e35a6556ffda71d03afe8c96 +0 -0
  124. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/b3/69579064bde9de9a19d114fc33e4e48cc8c0e4 +0 -0
  125. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/b5/61831c7dce8ea51e7ee6b6fa35745f14d8242d +0 -0
  126. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/b7/2c9e75ab7d0afe594664650aa8f6c772f5ac64 +0 -0
  127. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/bb/81ccc0513f18fc160b54a82861e9a80d23f4f6 +0 -0
  128. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/bb/934eb33bc1a8b85630bf680caffd99560c1b8f +0 -0
  129. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/bf/67907e337021ebff434e02b19b30a741c144af +0 -0
  130. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/bf/b54d65922ce1dfda1aaa014913a54e7172d0bc +0 -0
  131. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/c1/20fc812b9ad311c34a3608512d6a9d976bb48e +0 -0
  132. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/c1/397c6ed72c4e20ef6b9ab83163e9a6baba5b45 +0 -0
  133. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/c4/cba65f1163661999ee4b8ed23342b63bc1300c +0 -0
  134. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/c6/7f17e5707313600efcb85e9a3fedea35dba591 +0 -0
  135. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/c6/f32aced880bd165a251cb52b26b0c1107e2141 +0 -0
  136. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/cc/45df1d317a2eb63ff1ff3a5f3b4a9f98fd92b5 +0 -0
  137. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/cd/822b3574a88ebdd1ed82fd6983f37e626d52b4 +0 -0
  138. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/cf/0c0d9c6fb09473aaeb7f7e2edbd770c3f2ef3d +0 -0
  139. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/d2/992df305f4b56a466a2f221aeb182ddd20f418 +0 -0
  140. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/d6/39e8af592cd75a318d8affddd1bcc70c2095f2 +0 -0
  141. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/d6/9ab1c4aadf279936dd778e8346ba60f74705b6 +0 -0
  142. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/d8/4688b54c0040a30976b3a6540bc47adf7ce680 +0 -0
  143. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/d9/005f2cc7fc4e65f14ed5518276007c08cf2fd0 +0 -0
  144. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/d9/c2403fd166ce791b4e9d0c6792ed8342c71fcd +0 -0
  145. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/d9/dfa5aee51e92a541b707e8e7baea6f06deff98 +0 -0
  146. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/db/141dbaa93594df2a8156182f361ee4db829359 +0 -0
  147. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/db/3f2cd643292057936230b95cf7ec3046affe11 +0 -0
  148. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/db/ffa8ea7bda721d0cee7b9e4ce5b2ef927733ff +0 -0
  149. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/dc/c2bdbafb3296e09d9ee4955cfa55d275825f94 +0 -0
  150. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/dc/cdbd4266765d840be2ae35ab1752a0fa312c16 +0 -0
  151. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/dd/87fb5f606fe380d81e6fe3a2c98f9f99e3e09b +0 -0
  152. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/de/214c626ac2dd2685bfaa0bc0fc20f528d014d7 +0 -0
  153. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/df/e0770424b2a19faf507a501ebfc23be8f54e7b +0 -0
  154. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/e2/f2f8f4c25e62a297fc55f36acc6b01cfbab76f +0 -0
  155. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/e3/1356f90ea6dd0577b5e0b40b206319adcbf085 +0 -0
  156. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/e3/5a4dafc50850cacac7bf76c56db2715cbda2c4 +0 -0
  157. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/e4/6c715352db9fe3c887a635f1916df4ca1f4ff9 +0 -0
  158. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/e5/0580a0bd1e1b3d29f834382b80fceb61d5cf0c +0 -0
  159. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/e9/391ffe371f1cc43b42ef09b705d9c767c2e14f +0 -0
  160. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/ea/3a18cc75e53792744ef754e05d3f4481768c13 +0 -0
  161. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/ec/40fd8bf8e4c342534a9fc020289e402ba6bc9d +0 -0
  162. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/ec/d980279432b13f0374b90ca439a6329cdece0f +0 -0
  163. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/ee/cee64eacaff022dcdc509c0c2b1da492f21060 +0 -0
  164. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/f1/e50757fddc28b445545dc7e2759b54cdd0f42e +0 -0
  165. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/f4/b64d3107b39e3ad6f540c6607004ea34e6c024 +0 -0
  166. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/f4/ba7f815b886797b73fede071d86e0c134d2bc7 +0 -0
  167. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/f5/61c3c1bf1c9ea9c9d1f556a7be2869f71f3bdf +0 -0
  168. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/f6/44a8ff56fa035105fc517cbb1ac46c3d8e349a +0 -0
  169. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/f7/c98ba5c2f903e603b1f5e63d49fbc8a43815cc +0 -0
  170. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/f9/045a08e96eb76848fc4d68e3e3e687cca39a2d +0 -0
  171. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/fa/147e6bb78a2e8db241d231295fd7f1ed061af8 +0 -0
  172. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/objects/fc/292e793ecfd42240ac43be407023bd731fa9e7 +0 -0
  173. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/refs/heads/main +0 -0
  174. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/refs/remotes/origin/HEAD +0 -0
  175. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.git/refs/remotes/origin/main +0 -0
  176. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.gitattributes +0 -0
  177. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/.gitignore +0 -0
  178. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/LICENSE +0 -0
  179. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/README.md +0 -0
  180. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/__init__.py +0 -0
  181. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/brain_atlas.py +0 -0
  182. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/chat.py +0 -0
  183. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/correlators.py +0 -0
  184. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/.DS_Store +0 -0
  185. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/db2ls_sql_chtsht.json +0 -0
  186. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/docs_links.json +0 -0
  187. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/lang_code_iso639.json +0 -0
  188. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/example/style1.pdf +0 -0
  189. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/example/style2.pdf +0 -0
  190. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/example/style3.pdf +0 -0
  191. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/example/style4.pdf +0 -0
  192. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/example/style5.pdf +0 -0
  193. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/example/style6.pdf +0 -0
  194. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/example/style7.pdf +0 -0
  195. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/example/style8.pdf +0 -0
  196. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/example/style9.pdf +0 -0
  197. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style1.json +0 -0
  198. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style10.json +0 -0
  199. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style11.json +0 -0
  200. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style12.json +0 -0
  201. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style2.json +0 -0
  202. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style3.json +0 -0
  203. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style4.json +0 -0
  204. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style5.json +0 -0
  205. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style6.json +0 -0
  206. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style7.json +0 -0
  207. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style8.json +0 -0
  208. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/data/styles/style9.json +0 -0
  209. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/db2ls.py +0 -0
  210. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/doc.py +0 -0
  211. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/export_requirements.py +0 -0
  212. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/freqanalysis.py +0 -0
  213. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/ich2ls.py +0 -0
  214. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/netfinder.py +0 -0
  215. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/ocr.py +0 -0
  216. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/plot.py +0 -0
  217. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/setuptools-70.1.0-py3-none-any.whl +0 -0
  218. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/sleep_events_detectors.py +0 -0
  219. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/stats.py +0 -0
  220. {py2ls-0.1.10.24 → py2ls-0.1.10.26}/py2ls/translator.py +0 -0
  221. {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.24
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.10,<4.0
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
- # Helper to convert text or list of text to HTML
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
- # Convert each element into HTML and add line breaks
20
- html_content = "" # "<html><body>\n"
21
- html_content += "".join(
22
- text.replace("\n", "<br>") for text in text_list if text.strip()
23
- )
24
- # html_content += "\n</body></html>"
25
-
19
+ # Define a mapping for escape sequences to their HTML representations
20
+ escape_mapping = {
21
+ "\\": "&bsol;", # Backslash
22
+ "'": "&apos;", # Single quote
23
+ '"': "&quot;", # Double quote
24
+ "\n": "<br>", # Newline
25
+ "\r": "", # Carriage return (not represented in HTML)
26
+ "\t": "&nbsp;&nbsp;&nbsp;&nbsp;", # 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="/Users/macjianfeng/Dropbox/github/python/py2ls/confidential_data/gmail_login.json",
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
- subject="test",
45
- what="this is the body",
46
- attachments="/Users/test.pdf")
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 what
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
- "who" in k.lower(),
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
- ["wha" in k.lower(), "con" in k.lower(), "bod" in k.lower()]
132
+ [i in k.lower() for i in ["wha", "con", "bod"]]
82
133
  ): # 'what','content','body'
83
134
  what = v
84
- if any(["att" in k.lower(), "fil" in k.lower()]): # 'attachments', 'file'
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(["verb" in k.lower(), "show" in k.lower()]): # 'verbose', 'show'
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
- message["To"] = who
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="dataframe"' in str(attachments):
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
- part = MIMEBase("application", "octet-stream")
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
- server.sendmail(extract_kv()[0], who, message.as_string())
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>&copy; 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
+ "\\": "&bsol;", # Backslash
771
+ "'": "&apos;", # Single quote
772
+ '"': "&quot;", # Double quote
773
+ "\n": "<br>", # Newline
774
+ "\r": "", # Carriage return (not represented in HTML)
775
+ "\t": "&nbsp;&nbsp;&nbsp;&nbsp;", # 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(*args, **kwargs):
1817
+ def isa(content, kind):
1774
1818
  """
1775
- fpath, contains='img'
1776
- containss file paths based on the specified contains.
1819
+ content, kind='img'
1820
+ kinds file paths based on the specified kind.
1777
1821
  Args:
1778
- fpath (str): Path to the file.
1779
- contains (str): contains of file to contains. Default is 'img' for images. Other options include 'doc' for documents,
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 contains, False otherwise.
1826
+ bool: True if the file matches the kind, False otherwise.
1783
1827
  """
1784
- for arg in args:
1785
- if isinstance(arg, str):
1786
- if "/" in arg or "\\" in arg:
1787
- fpath = arg
1788
- else:
1789
- contains = arg
1790
- if "img" in contains.lower() or "image" in contains.lower():
1791
- return is_image(fpath)
1792
- elif "doc" in contains.lower():
1793
- return is_document(fpath)
1794
- elif "zip" in contains.lower():
1795
- return is_zip(fpath)
1796
- elif "dir" in contains.lower() or (
1797
- "f" in contains.lower() and "d" in contains.lower()
1798
- ):
1799
- return os.path.isdir(fpath)
1800
- elif "fi" in contains.lower(): # file
1801
- return os.path.isfile(fpath)
1802
- elif "num" in contains.lower(): # file
1803
- return os.path.isfile(fpath)
1804
- elif "text" in contains.lower() or "txt" in contains.lower(): # file
1805
- return is_text(fpath)
1806
- elif "color" in contains.lower(): # file
1807
- return is_str_color(fpath)
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"{contains} was not set up correctly")
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.24"
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.10"
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