From 274198cdc3c4b5231728d43e39f3f6ee8d037fc3 Mon Sep 17 00:00:00 2001 From: E3V3A Date: Sun, 23 Jan 2022 03:08:07 +0200 Subject: [PATCH] :green_heart: New Release and bug fixes: * Bump version v1.0.4 * Fixed PR #11 for venv exception handling (fixes #10) * Fixed pyOSinfo uname issue. * Fixed ANSI color capability check in is_posix() (fix #18) (May have broken on older Windows machines or Python versions.) * Fixed wrong Windows-10 creation and modification times ([cm]Times) (Need feedback from native *nix users.) * Added new script pip-search to fix #17 * Changed Shebangs from 'python3' to 'python' * Updated README Changes: deleted: .github/ISSUE_TEMPLATE/bug_report-md.md modified: .github/ISSUE_TEMPLATE/bug_report.md new file: .github/ISSUE_TEMPLATE/config.yml modified: CHANGES.txt modified: README.md modified: pip-date modified: pip-describe new file: pip-search.py modified: pipbyday modified: pyOSinfo modified: pyfileinfo modified: setup.py --- .github/ISSUE_TEMPLATE/bug_report-md.md | 45 ------- .github/ISSUE_TEMPLATE/bug_report.md | 67 ++++++---- .github/ISSUE_TEMPLATE/config.yml | 1 + CHANGES.txt | 16 ++- README.md | 12 +- pip-date | 152 ++++++++++++---------- pip-describe | 12 +- pip-search.py | 166 ++++++++++++++++++++++++ pipbyday | 6 +- pyOSinfo | 82 ++++++------ pyfileinfo | 28 ++-- setup.py | 21 +-- 12 files changed, 396 insertions(+), 212 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report-md.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100755 pip-search.py diff --git a/.github/ISSUE_TEMPLATE/bug_report-md.md b/.github/ISSUE_TEMPLATE/bug_report-md.md deleted file mode 100644 index defc010..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report-md.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -name: bug_report.md -about: Create a report to help us improve -title: '' -labels: '' -assignees: '' - ---- - ---- -name: Bug report -about: Create a report to help us improve - ---- - -**Environment** - -* pip version: -* Python version: -* OS: - - - -**Description** - - -**Expected behavior** - - -**How to Reproduce** - - -1. Get package from '...' -2. Then run '...' -3. An error occurs. - -**Output** - -``` -Paste the output of the steps above, including the commands themselves and -pip's output/traceback etc. -``` - -If the problem is graphical in nature, please add a screenshot. -(A picture is worth...) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea7..15e3b50 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,38 +1,59 @@ --- -name: Bug report +name: Bug Report about: Create a report to help us improve title: '' labels: '' assignees: '' + --- +For new bugs and issues, please **make sure** to: +- [ ] check and search previous issues for similar or related problems. +- [ ] get info about your system configuration and environment, if relevant. +- [ ] include detailed information on what you did before the *error/issue* occurred. +- [ ] use code mark-down using 3 back-ticks (\`\`\`), to enclose multi-line code/input/output. +- [ ] include a **screenshot** for issues concerning *layouts, formatting* or other UI stuff. +- [ ] If this is a request for an enhancement or other improvement, please state this clearly. + + +**Environment** +Please provide some information about your computer environment: + +- [ ] OS: + `$W = (Get-CimInstance Win32_OperatingSystem); '{0} ({1})' -f $W.Caption, $W.Verson` (On Windows) + `uname -a` (On WSL or *nix based OS) +- [ ] Python version: + `python -VV && python -c "import os; print('\n'.join([os.name, os.sys.platform]));"` +- [ ] pip version: + `pip -V` +- [ ] Terminal/Shell: (*powershell, pwsh, Windows Terminal, WSL* etc.) -**Describe the bug** -A clear and concise description of what the bug is. -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error + + +**Description** + **Expected behavior** -A clear and concise description of what you expected to happen. + + +**Actual Behaviour:** + + + +**How to Reproduce** + -**Screenshots** -If applicable, add screenshots to help explain your problem. +1. Get package from '...' +2. Then run '...' +3. An error occurs. -**Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] +**Output** -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] +``` +Paste the output of the steps above, including the commands themselves and +pip's output/traceback etc. +``` -**Additional context** -Add any other context about the problem here. +If the problem is graphical in nature, please add a screenshot. +(A picture is worth...) diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..3ba13e0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1 @@ +blank_issues_enabled: false diff --git a/CHANGES.txt b/CHANGES.txt index f9309a7..49ce07d 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,10 +1,21 @@ +v1.0.4, 2022-01-22 -- New Release and bug fixes: + +* Fixed PR #11 for venv exception handling +* Fixed pyOSinfo uname issue. +* Fixed ANSI color capability check in is_posix() + (May have broken on older Windows machines or Python versions.) +* Fixed wrong Windows-10 creation and modification times ([cm]Times) + (Need feedback from native *nix users.) +* Added new script pip-search +* Changed Shebangs from 'python3' to 'python' +* Updated README +* Bump version v1.0.4 v1.0.4, 2022-01-22 -- Sync of old work -* Synchronisation of previous work (2019-02-15), not pushed to repo. +* Synchronisation of previous work (2019-02-15) not pushed to repo. New Release and bug fixes: - * Added __main__ function coding style * Added __author__ etc. * Added new search tool: pip-search @@ -14,7 +25,6 @@ New Release and bug fixes: * Removed redundant whitespace not affecting readability * Fixed wrong date in CHANGES.txt - v1.0.3, 2019-02-08 -- New Release and bug fixes: * Fixed path search mechnaism for full Linux support. Fixed #6 diff --git a/README.md b/README.md index 643f59c..e5c17a9 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ A simple *Python3* CLI tool to show the installation or modification times of al | STATUS: | Version | Date | Maintained? | |:------- |:------- |:---- |:----------- | -| Working | `1.0.3` | 2019-02-08 | YES | +| Working | `1.0.4` | 2022-01-22 | YES | --- @@ -93,6 +93,10 @@ for a given file using python's `os.stat` info. * A script called **`pyOSinfo`**, that will print a number of *os, system* and *platform* variables, as seen by your Python interpreter. +* **`NEW`** A script called (hold your breath!) **`pip-search`**, that will download and +search all of the PyPi package database for packages matching your search criteria. +However, for colored and sexy package searches, I recommend using [pip_search](https://github.com/victorgarric/pip_search). +Also, for cool powershell wrapper of pip-search, read [this](https://github.com/E3V3A/pip-date/issues/17#issuecomment-1019325517). **Q:** *Will I continue to support this tool?* @@ -105,6 +109,7 @@ something just send me a PR, or at the very least, a detailed code snippet of wh * [requests](https://github.com/requests/requests) - used by `pip-describe` to get PyPI info +* [lxml](https://github.com/lxml/lxml) - used by `pip-search` to parse html from PyPI and what you already have: * [Python3](https://www.python.org/) @@ -203,11 +208,12 @@ For all the gory details, see: --- -#### Recommeded Similar Tools: +#### Recommeded or Similar Tools: - **[pip-check](https://github.com/bartTC/pip-check/)** - Check you pip package update status with nice ANSI colored CLI - **[pip-chill](https://github.com/rbanffy/pip-chill)** - Lists only the dependencies (or not) of installed packages - +- **[pip_search](https://github.com/victorgarric/pip_search)** - Victor's amazing pip search replacement +- **[venvlink](https://github.com/np-8/venvlink)** - Using virtual environments outside of the project folder, like a boss! --- #### Bugs and Warnings diff --git a/pip-date b/pip-date index ecb1c2b..08e872b 100755 --- a/pip-date +++ b/pip-date @@ -1,13 +1,13 @@ -#!/usr/bin/env python3 -# pip-date - Show the install date of all pip-installed python3 packages +#!/usr/bin/env python # -*- coding: utf-8 -*- -# --------------------------------------------------------------------- +#---------------------------------------------------------------------- # Author: E:V:A # Date: 2018-02-15 # Version: 1.0.4 # License: GPLv3 # URL: https://github.com/E3V3A/pip-date/ -# --------------------------------------------------------------------- +# Description Show the install date of all pip-installed python3 packages +#---------------------------------------------------------------------- # ToDo: # [ ] better RegEx for "pip" # [/] add flake8 QA ignore comments @@ -22,7 +22,7 @@ # - [ ] '-t ' : To highlight packages installed ago # # NOTES: -# --------------------------------------------------------------------- +#---------------------------------------------------------------------- # [a/c/m]time # ------------------------------------------------------------------ # On Windows (via Cygwin & Python3): @@ -35,11 +35,11 @@ # The access time is: aTime (normally not used) # # ==> For seeing last modification time, use "cTime" on Windows FS's, and "mTime" on *linux FS's -# --------------------------------------------------------------------- +#---------------------------------------------------------------------- # References: # [1] https://linuxhandbook.com/file-timestamps/ # [2] https://www.unixtutorial.org/atime-ctime-mtime-in-unix-filesystems/ -# --------------------------------------------------------------------- +#---------------------------------------------------------------------- import re, os, sys, platform # noqa: E401 #import subprocess import site, pkg_resources # noqa: E401 @@ -48,32 +48,39 @@ from datetime import timedelta #from time import strftime __author__ = "E:V:A (E3V3A)" -__copyright__ = "GPLv3 2019" +__copyright__ = "GPLv3 2022" #__credits__ = ["https://github.com/E3V3A/pip-date/"] -__version__ = '1.0.3' +__version__ = '1.0.4' -#------------------------------------------------ +#---------------------------------------------------------- # OS Check-1 -#------------------------------------------------ -# Apparently for: (Winpython64-3.6.7.0Zero) -# [1] D:\wimpy\wpy\python-3.6.7.amd64\python.exe -c "import os,sys; print('TERM=%s' % os.getenv('TERM'));" -# [2] C:\cygwin64\bin\python3.6m.exe -c "import os,sys; print('TERM=%s' % os.getenv('TERM'));" -# PowerShell/CMD Windows python: TERM=None [1] -# PowerShell/CMD Cygwin python: TERM=cygwin [2] +#---------------------------------------------------------- +# Apparently for: +# python.exe -c "import os,sys; print('TERM=%s' % os.getenv('TERM'));" +# python -c "import os; print('\n'.join([os.name, os.sys.platform]));" +# PowerShell/CMD Windows python: TERM=None +# PowerShell/CMD Cygwin python: TERM=cygwin +# WSL TERM=xterm-256color +#---------------------------------------------------------- def is_posix(): - px_term = os.getenv("TERM") # [cygwin, xterm, xterm-color, xterm-256color] + noco = ('dumb', 'xtermm', 'xterm-mono') # dumb = No VT, 'others' --> $PSStyle.OutputRendering = PlainText + px_term = os.getenv('TERM') # [cygwin, xterm, xterm-color, xterm-256color] px_name = os.name # [posix, nt, ...] px_plat = sys.platform # [linux, cygwin, win32] - if ((px_term == "None") or (px_term == "")): # For native Windows "consoles" ofet return "None", since ="". - return False - if (("posix" not in px_name) and ("win32" in px_plat)): + if px_term in noco: return False + if ((px_term == 'None') or (px_term == '')): # For native Windows "consoles" often return "None", since ="". + if ( (px_name == 'nt') and (px_plat == 'win32') ): + return True + else: + return False + # Assume we have a color term return True -#------------------------------------------------ +#---------------------------------------------------------- # OS Check-2 -#------------------------------------------------ +#---------------------------------------------------------- # We need to test how [a/c/m]time works on the OS def isWinFS(): if platform.architecture()[1] == "WindowsPE": @@ -83,16 +90,14 @@ def isWinFS(): print("Using mTime for Linux FS\n") return False -#------------------------------------------------ +#---------------------------------------------------------- # Text Coloring -#------------------------------------------------ +#---------------------------------------------------------- # Usage: print(yellow("This is yellow")) def color(text, color_code): - #if self.nposix: if not is_posix(): return text - # for brighter colors, use "1;" in front of "color_code" bright = '' # '1;' return '\x1b[%s%sm%s\x1b[0m' % (bright, color_code, text) @@ -107,9 +112,9 @@ def purple(text): return color(text, 35) # aka. magenta # noqa def cyan(text): return color(text, '0;49;96') # 36 # noqa def white(text): return color(text, '0;49;97') # bright white # noqa -#------------------------------------------------ +#---------------------------------------------------------- # Print Usage -#------------------------------------------------ +#---------------------------------------------------------- def usage(): print(" Usage: %s\n" % os.path.basename(__file__)) print(" This will return a detailed sorted list of all your installed packages.") @@ -122,9 +127,9 @@ def usage(): print(" License: GPLv3\n") sys.exit(2) -#------------------------------------------------ +#---------------------------------------------------------- # Print Warning -#------------------------------------------------ +#---------------------------------------------------------- def print_warning(): print('\n') print('-'*60) @@ -139,9 +144,9 @@ def print_warning(): #print(' (Usally to \"xterm\".)') print('-'*60) -#------------------------------------------------ +#---------------------------------------------------------- # Print Color Legend -#------------------------------------------------ +#---------------------------------------------------------- # See: # https://github.com/PowerShell/PowerShell/issues/8409 # https://en.wikipedia.org/wiki/Code_page_437 @@ -150,6 +155,7 @@ def print_warning(): # Let's try: # 2585, # Look best but is not part of cp437 and thus font dependent & not widely available # 2580, 25A0, 2588 # IBM-437 +#---------------------------------------------------------- def print_legend(): #cc = u'\u2585' # Unicode Character for a "5/8th box" # (U+2585) is not part of IBM-437 #cc = u'\u2588' # Unicode Character for a "full box" # (U+2588) is part of IBM-437 @@ -162,9 +168,9 @@ def print_legend(): print(" {} = Non-PEM-compliant Version string (PEP-0440) | ~/.local install".format(green(cc))) print(" {} = A 'setuptools' dependency package".format(blue(cc))) -#------------------------------------------------ +#---------------------------------------------------------- # Helper Functions -#------------------------------------------------ +#---------------------------------------------------------- def safe_name(name): # Replace runs of non-alphanumeric characters with a single '-'. return re.sub('[^A-Za-z0-9]+', '-', name) @@ -210,6 +216,7 @@ def is_canonical(version): return re.match(canrex, version) is not None def pkgcol(pkgarr): + #---------------------------------------------------------- # The color require special treatment, because # of sorting on key position and getting ljust space. # https://packaging.python.org/key_projects/ @@ -219,7 +226,7 @@ def pkgcol(pkgarr): # NOTE! # We can't use "pip" because the we're only checking if the string is present in line, # Thus anything with "pip" in it would be caught, so we need a smarter RE here. - + #---------------------------------------------------------- line = '' for i in range(len(pkgarr)): line = pkgarr[i] @@ -233,9 +240,9 @@ def pkgcol(pkgarr): break return pkgarr -#------------------------------------------------ +#---------------------------------------------------------- # MAIN -#------------------------------------------------ +#---------------------------------------------------------- def main_func(): print() @@ -244,22 +251,23 @@ def main_func(): pcnt = 0 pkg = [] - #------------------ + #--------------------------------------- # CLI arguments - #------------------ + #--------------------------------------- narg = len(sys.argv) - 1 if narg >= 1: #pkg = sys.argv[1] usage() #if is_posix(): # nposix = 1 - #------------------ + #--------------------------------------- # Check [a/c,m]time availability useWinStat = False # Linux ELF based FS system if isWinFS(): useWinStat = True # WindowsPE FS system + #--------------------------------------- # MacOS: ?? # Cygwin: native python, we only have 1 location: # /usr/lib/python3.6/site-packages @@ -269,11 +277,14 @@ def main_func(): # /usr/lib/python3.6/dist-packages # Also add the unprivileged user's local package location: # $HOME/.local/lib/python3.6/site-packages/ - site_loc = site.getsitepackages() # [...] - if debug: print("site_locs (site): ", site_loc) - site_loc += [site.getusersitepackages()] # add $HOME/.local/lib/python3.6/site-packages/ - if debug: print("site_locs (all): ", site_loc) - + #--------------------------------------- + try: + site_loc = site.getsitepackages() # [...] + if debug: print("site_locs (site): ", site_loc) + site_loc += [site.getusersitepackages()] # add $HOME/.local/lib/python3.6/site-packages/ + if debug: print("site_locs (all): ", site_loc) + except AttributeError: + site_loc = site.USER_SITE for d in pkg_resources.working_set: @@ -299,9 +310,9 @@ def main_func(): except ValueError as e: print(red("ERROR:") + " %s" % e) - #---------------------------------- + #--------------------------------------- # Get the correct package location - #---------------------------------- + #--------------------------------------- # Because d.location doesn't return a file, but only a directory, # for certain packages, we also check the "module directory" ??? try: @@ -315,7 +326,7 @@ def main_func(): os.stat(pkg_loc) except OSError: pkg_loc = d.location - #---------------------------------- + #--------------------------------------- if debug: print('-'*40) @@ -329,9 +340,9 @@ def main_func(): print(purple("Found Bad Path Location for:") + " %s" % white(pkg_name)) print("Package Location found at: %s" % (pkg_loc)) - #---------------------------------- + #--------------------------------------- # Getting OS Dependent TimeStamps - #---------------------------------- + #--------------------------------------- # NOTE: # (1) [acm]time as used in variable names HERE, is true for LinuxFS, # but swapped for WindowsFS's @@ -342,13 +353,14 @@ def main_func(): # aTime = access time ... - Rarely used because of FS performance # mtime = modification time - # ctime = creation time - is the "real" last modification time on windows - - + #--------------------------------------- if debug: print("pkg_loc: %s" % pkg_loc) + if os.path.exists(pkg_loc): if useWinStat: - tsc = os.path.getctime(pkg_loc) # swap! WindowsFS: "creation" time is "atime" - tsm = os.path.getatime(pkg_loc) # swap! WindowsFS: "modification" time is "mtime" + tsc = os.path.getctime(pkg_loc) # WindowsFS: ctime: "creation time" + tsm = os.path.getmtime(pkg_loc) # WindowsFS: mtime: "last modified" + #tsm = os.path.getatime(pkg_loc) # WindowsFS: atime: "last accessed" else: tsc = os.path.getctime(pkg_loc) # LinuxFS (ctime) tsm = os.path.getmtime(pkg_loc) # LinuxFS (mtime) @@ -357,9 +369,14 @@ def main_func(): print(red("Skipping Bad Path of:") + " %s: \t%s" % (pkg_name, pkg_loc)) # to_filename(pkg_name)) continue - #---------------------------------- + #------------------------------------------------------------------------------------ # Processing Time Stamps - #---------------------------------- + #------------------------------------------------------------------------------------ + # Logic: + # 1. IF ( mTime > (K * cTime) ) THEN highlight mTime ELSE don't show # (1) + # 2. IF ( cTime < '1-week-ago' ) THEN highlight cTime # (2) + # + #------------------------------------------------------------------------------------ pkg_ctime = datetime.fromtimestamp(tsc).strftime("%Y-%m-%d %H:%M:%S").strip() # str pkg_mtime = datetime.fromtimestamp(tsm).strftime("%Y-%m-%d %H:%M:%S").strip() # str @@ -368,16 +385,16 @@ def main_func(): tdelta = abs(tsc - tsm) # Calculate time difference #if pkg_ctime != pkg_mtime: # This is too restrictive (OS need seconds to install) if tdelta > max_tdelta: # - pkg_mtime = yellow(pkg_mtime) + pkg_mtime = yellow(pkg_mtime) # (1) Highlight packages with different creation vs modification Times else: - pkg_mtime = '' + pkg_mtime = '' # Skip those where: mTime ~= cTime - ctNow = datetime.now() # Time "now" - ct7dy = ctNow - timedelta(days=7) # Time 1 week ago - pctim = datetime.fromtimestamp(tsc) # Time file cTime Time Stamp + ctNow = datetime.now() # Time: "now" + ct7dy = ctNow - timedelta(days=7) # Time: 1-week-ago + pctim = datetime.fromtimestamp(tsc) # Time: file cTime (Time Stamp) if pctim > ct7dy: - pkg_ctime = cyan(pkg_ctime) # cyan - #---------------------------------- + pkg_ctime = cyan(pkg_ctime) # (2) Highlight packages recently created (cTime < 1-week-ago) + #------------------------------------------------------------------------------------ if pkg_pre: pkg_pre = yellow(pkg_pre) + " " # [egg,...] @@ -389,16 +406,17 @@ def main_func(): if 'bdist_wheel' in pkg_whl: pkg_typ = 'wheel' else: - pkg_typ = white('sdist') + #pkg_typ = white('sdist') + pkg_typ = orange('sdist') pkg_loc = test_loc(pkg_loc) pcnt += 1 - pkg += ["{:20} {:<20} {:<20} {:<16} {:6} {:<4} {:5} {:3}".format(pkg_name.ljust(20,' '), pkg_ctime,pkg_mtime,pkg_ver,pkg_ins,pkg_pre,pkg_typ,pkg_loc)] # noqa + pkg += ["{:20} {:<20} {:<20} {:<16} {:6} {:<4} {:5} {:3}".format(pkg_name.ljust(20,' '), pkg_ctime, pkg_mtime, pkg_ver, pkg_ins, pkg_pre, pkg_typ, pkg_loc)] # noqa if useWinStat: - # header_str = "{:20} {:20} {:20} {:16} {:6} {:4} {:5} {:3}".format('Package'.ljust(20, ' '), 'Installed (cTime)', 'Modified (mTime)', 'Version', 'Inst', 'Prec', 'Type ', 'Loc') - header_str = "{:20} {:20} {:20} {:16} {:6} {:4} {:5} {:3}".format('Package'.ljust(20, ' '), 'LastModified (mTime)', 'FirstSeen (aTime)', 'Version', 'Inst', 'Prec', 'Type ', 'Loc') + # header_str = "{:20} {:20} {:20} {:16} {:6} {:4} {:5} {:3}".format('Package'.ljust(20, ' '), 'LastModified (mTime)', 'FirstSeen (aTime)', 'Version', 'Inst', 'Prec', 'Type ', 'Loc') + header_str = "{:20} {:20} {:20} {:16} {:6} {:4} {:5} {:3}".format('Package'.ljust(20, ' '), 'Installed (cTime)', 'LastModified (mTime)', 'Version', 'Inst', 'Prec', 'Type ', 'Loc') else: header_str = "{:20} {:20} {:20} {:16} {:6} {:4} {:5} {:3}".format('Package'.ljust(20, ' '), 'LastModified (mTime)', 'FirstSeen (cTime)', 'Version', 'Inst', 'Prec', 'Type ', 'Loc') diff --git a/pip-describe b/pip-describe index 50cfaa4..bfe3d3a 100755 --- a/pip-describe +++ b/pip-describe @@ -1,5 +1,4 @@ -#!/usr/bin/env python3 -# pip-describe - Show full text package description from PyPI +#!/usr/bin/env python # -*- coding: utf-8 -*- #---------------------------------------------------------------------- # File Name : pip-describe @@ -8,9 +7,10 @@ # Version : 1.0.1 # License : GPLv3 # URL : https://github.com/E3V3A/pip-date +# Description: : Show full text package description from PyPI #---------------------------------------------------------------------- -# NOTE: We don't do the summary as it is already available -# from the `pip search` results. +# NOTE: We don't do the summary as it is already available +# from the `pip search` results. # [ ] But we can always add CLI switch `-s` for summary #---------------------------------------------------------------------- import os, sys @@ -20,7 +20,7 @@ try: import requests as req # exception ImportError as err: # 3.3 except ModuleNotFoundError as err: # 3.6 - print("\nThis program need the \"requests\" package to work.") + print("\nThis program need the \"requests\" package to work.") print("Please download and install from:\nhttps://github.com/requests/requests") print(err) sys.exit(1) @@ -47,7 +47,7 @@ if res.status_code == 200: dat = res.json() #smm = dat['info']['summary'].strip() # (short) description des = dat['info']['description'].strip() # long_description (often full README) - print("\nPackage Description:") + print("\nPackage Description:") print("-"*80) print("%s" % des) print("-"*80) diff --git a/pip-search.py b/pip-search.py new file mode 100755 index 0000000..0b586c8 --- /dev/null +++ b/pip-search.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +#---------------------------------------------------------------------- +# File Name : pip-search.py +# Author : E:V:A +# Last Modified : 2022-01-22 +# Version : 1.0.1 +# License : GPLv3 +# URL : https://github.com/E3V3A/pip-date +# Description : Getting a list of pip packages matching a string +# +# References: +# [1] https://docs.python.org/3/howto/regex.html +# [2] https://github.com/victorgarric/pip_search +# [3] +# +#---------------------------------------------------------------------- +# ToDo: +# [ ] Add color +# [ ] Add '-n ' comand line switch to show max (max_shown) matches. +# [ ] Put result list in local file under $USERPROFILE (Win) or $HOME (*nix) +# [ ] In windows search type: "manage app execution aliases" +#---------------------------------------------------------------------- +# +# Getting a list of pip packages +# curl -i -X OPTIONS -H 'Accept: application/json' -H "Content-Type: application/json" https://pypi.org/simple/ >pypilist.html +# cat pypilist.html | grep -io '' | sed 's/\(\)//g' >plink.txt +# sed -e 's/^\/simple\///' |sed -e 's/.$//' +#---------------------------------------------------------------------- +# +# python -c "a='ars*'; print('yes') if('*' in a) else print('no');" +# if ('*' in arg): +# rep = r'{}'.format(arg) +# else: +# rep = r'.*{}.*'.format(arg) +#---------------------------------------------------------------------- +import os, re, sys +import datetime +import requests +from lxml import html + +debug = 0 +#TS = '{:%Y%m%d_%H%M%S}'.format(datetime.datetime.now()) +TS = '{:%Y%m%d}'.format(datetime.datetime.now()) +filename = 'tmp_piplist_{}.txt'.format(TS) + +showline = ' '+'-'*60 + +arg = "pyt" +arg = sys.argv[1] # CLI provided search string (args[0]) +rep = r'.*{}.*'.format(arg) # pattern +rec = re.compile(rep, re.I) # compiled + +my_headers = {'user-agent': 'curl/7.55.1','accept': 'application/json', 'content-type': 'application/json', 'referer': 'https://pypi.org/', 'cache-control': 'no-cache', 'connection': 'close'} +name_list = [] +match_list = [] + +#---------------------------------------------------------------------- +# Utilitiy Functions +#---------------------------------------------------------------------- +def print_warn(): + print('\n Warning!') + print(' Searching all ~350,000 pip packages can take a very long time!') + print(' This script will first download the 19 MB (HTML) file, and only') + print(' then search the list for the content requested.') + print(' This can take up to 20 seconds.\n') + + +def save_list(file, data): + print(' Saving package list to file:\n ./{} '.format(file)) + if os.path.exists(file): + print(' WARNING: The file already exists, so skipping.') + else: + f = open(file, 'w') + for i in data: + f.write('{}\n'.format(i)) + f.close() + +def load_list(file): + data = [] + print(' Trying to load package list from file...',end='') + if os.path.exists(file): + f = open(file, 'r') + #with open(file, 'r') as f: + for x in f: + item = x[:-1] + data.append(item) + f.close() + print('ok\n ./{}'.format(file)) + else: + print('FAIL\n Previous package list file is too old or doesn\'t exist!') + #print(' (./{})'.format(file)) + if (debug): print('\nDATA:\n{}\n...\n{}\n\n'.format(data[0:20], data[len(data)-100:])) + return data + +def download_pip_list(): + name_list = [] + #name_list = '' + print('\n Downloading full pip list... ', end='') + with requests.Session() as s: + try: + r = s.get('https://pypi.org/simple/', headers=my_headers) + except: + pass + print('ok') + + tree = html.fromstring(r.content) # Use lxml to get package names + package_list = [package for package in tree.xpath('.//a/@href')] # Grab the part + #print(showline) + print(' Found {:,} packages in current list.\n'.format(len(package_list))) + + if (debug): print(package_list[1:30]) + + p = re.compile(r'/simple/(.*)/') # Only get the pip package name + #name_list = list(filter(p.match, package_list)) # Maybe try using "filter" to match + for i in package_list: + item = p.match(i) + name_list.append(item.group(1)) + + if (debug): + print(' Package name list is now clean, with {:,} items.\n'.format(len(name_list))) + print('\nDATA:\n{}\n...\n{}\n\n'.format(name_list[0:20], name_list[len(name_list)-100:])) + return name_list + +def print_matches(name_list): + # Print matching package items + j=0 + for i in name_list: + m = rec.search(i) + if not (m == None): + #print(m[0]) + match_list.append(m[0]) + j += 1 + + #print(showline) + print('\n Found {:,} matches in current list.'.format(j)) + if (j >= 60): + print('\n Only showing first 60 matches of list.') # ToDo: .format(max_shown)) + print(' Try to narrow your search or use regex.') + print(showline) + #print(match_list[1:60]) + for x in match_list[1:60]: print(' {}'.format(x)); + else: + print(showline) + #print(match_list) + for x in match_list: print(' {}'.format(x)); + print(showline) + #print('\n') + +#---------------------------------------------------------------------- +# Main +#---------------------------------------------------------------------- +print_warn() + +name_list = load_list(filename) +if not (name_list): + name_list = download_pip_list() + save_list(filename, name_list) + +print_matches(name_list) +print('ok\n') + + +#---------------------------------------------------------------------- +# EOF +#---------------------------------------------------------------------- diff --git a/pipbyday b/pipbyday index 046ff8a..fe24d62 100755 --- a/pipbyday +++ b/pipbyday @@ -1,6 +1,6 @@ -#!/usr/bin/env python3 +#!/usr/bin/env python # -*- coding: utf-8 -*- -# pipbyday- Prints when python packages were installed +# pipbyday - Prints when python packages were installed #---------------------------------------------------------------------- # File Name : pipbyday # Author : E:V:A @@ -53,7 +53,7 @@ if __name__ == "__main__": mtime = os.path.getctime(pkg_loc) # modification time on Windows else: mtime = os.path.getmtime(pkg_loc) # modification time on Linux - + mtime = datetime.fromtimestamp(mtime).strftime("%Y-%m-%d %H:%M:%S") #packages.append([mtime, pkg_nam_ver]) packages.append([mtime, pkg_nam_ver, pkg_loc]) diff --git a/pyOSinfo b/pyOSinfo index 4e09e1b..fe3889f 100755 --- a/pyOSinfo +++ b/pyOSinfo @@ -1,35 +1,36 @@ -#!/usr/bin/env python3 -# pyOSinfo - Show what Python3 thinks about your system environment +#!/usr/bin/env python # -*- coding: utf-8 -*- +# pyOSinfo - Show what Python thinks about your system environment #---------------------------------------------------------------------- # File Name : pyOSinfo # Author : E:V:A -# Last Modified : 2018-02-08 updated 2020-11-07 -# Version : 1.0.1 +# Date : 2018-02-08 +# Last Modified : 2022-01-23 +# Version : 1.0.2 # License : GPLv3 # URL : https://github.com/E3V3A/pip-date/ # Description : Show some system, os and platform information as seen by python3 #---------------------------------------------------------------------- -# NOTE: -# On Cygwin, yuo have to pay attention to the parts of the PATH -# that is cpoied fromt he Windows (System PATH). This will interfere -# with the resulsts of 'os.name' [nt vs posix], depeding on which -# python*.exe was used. Try to test with: -# ls -al `which python.exe` +# NOTE: +# On Cygwin, yuo have to pay attention to the parts of the PATH +# that is cpoied fromt he Windows (System PATH). This will interfere +# with the resulsts of 'os.name' [nt vs posix], depeding on which +# python*.exe was used. Try to test with: +# ls -al `which python.exe` #---------------------------------------------------------------------- import os, sys, platform, site from os.path import join -__version__ = '1.0.1' +__version__ = '1.0.2' TRUECOLOR = "\x1b[38;2;255;100;0mTRUECOLOR\x1b[0m" -def hasUname(): - try: - os.uname()[0] - return True - except: - return False +def hasUname(): + try: + os.uname()[0] + return True + except: + return False print("\nCurrent OS variables as seen by Python3 (%s)\n" % platform.python_version() ) print('os.getenv(\"TERM\"): %s' % os.getenv("TERM")) # [xterm, xterm-color, xterm-256color] @@ -42,35 +43,36 @@ print('TRUECOLOR (orange): %s' % TRUECOLOR ) # TRUECOLOR written in # Like: Winpython64-3.6.7.0Zero print('\nos.uname:') # sysname, nodename, release, version, machine if hasUname(): - print('\tnodename: %s' % os.uname()[1] ) # - print('\tmachine: %s' % os.uname()[4] ) # - print('\tsysname: %s' % os.uname()[0] ) # - print('\trelease: %s' % os.uname()[2] ) # - print('\tversion: %s' % os.uname()[3] ) # -else: - print('\tN/A' ) + print('\tnodename: %s' % os.uname()[1] ) # + print('\tmachine: %s' % os.uname()[4] ) # + print('\tsysname: %s' % os.uname()[0] ) # + print('\trelease: %s' % os.uname()[2] ) # + print('\tversion: %s' % os.uname()[3] ) # +else: + print('\tN/A' ) print('\nplatform:') -print('\tnode: %s' % platform.node() ) -print('\tmachine: %s' % platform.machine() ) -print('\tprocessor: %s' % platform.processor() ) -print('\tsystem: %s' % platform.system() ) -print('\trelease: %s' % platform.release() ) -print('\tversion: %s' % platform.version() ) -print('\tplatform: %s' % platform.platform() ) -print('\n\tuname (6): (%s,%s,%s,%s,%s,%s)' % platform.uname() ) # (system, node, release, version, machine, processor) -print('\tarchitecture (2): (%s,%s)' % platform.architecture() ) # (bits, linkage) -#print('\twin32_ver (4): (%s,%s,%s,%s)' % platform.win32_ver() ) # (release, version, csd, ptype) +print('\tnode : %s' % platform.node() ) +print('\tmachine : %s' % platform.machine() ) +print('\tprocessor : %s' % platform.processor() ) +print('\tsystem : %s' % platform.system() ) +print('\trelease : %s' % platform.release() ) +print('\tversion : %s' % platform.version() ) +print('\tplatform : %s' % platform.platform() ) +print('\tuname (6) : {}'.format(platform.uname()) ) # (system, node, release, version, machine) # not processor +print('\tarchitecture (2): (%s,%s)' % platform.architecture() ) # (bits, linkage) +#print('\twin32_ver (4): (%s,%s,%s,%s)' % platform.win32_ver() ) # (release, version, csd, ptype) print("\nsite:") print("\tgetusersitepackages: %s" % site.getusersitepackages()) i=0 -for ploc in site.getsitepackages(): # Returns a [] - print("\tgetsitepackages[%i]: %s" % (i, ploc)) # site.getsitepackages()[0]) - i+=1 -print("\n\tPREFIXES (2): %s" % site.PREFIXES) -print("\tUSER_SITE: %s" % site.USER_SITE) -print("\tUSER_BASE: %s" % site.USER_BASE) +for ploc in site.getsitepackages(): # Returns a [] + print("\tgetsitepackages[%i]: %s" % (i, ploc)) # site.getsitepackages()[0]) + i+=1 +print() +print("\tPREFIXES (2) : %s" % site.PREFIXES) +print("\tUSER_SITE : %s" % site.USER_SITE) +print("\tUSER_BASE : %s" % site.USER_BASE) print('\nsys.path:\n\t%s\n' % ('\n\t'.join(sys.path)) ) diff --git a/pyfileinfo b/pyfileinfo index b1f81b7..5d5570b 100755 --- a/pyfileinfo +++ b/pyfileinfo @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# pyfileinfo - Get detailed file info from "stat" of file # -*- coding: utf-8 -*- +# pyfileinfo - Get detailed file info from "stat" of file #---------------------------------------------------------------------- # File Name : pyfileinfo # Author : E:V:A @@ -9,8 +9,8 @@ # License : GPLv3 # URL : https://github.com/E3V3A/pip-date # Description : Show detailed file information for a given file using python's os.stat info -# -# References: +# +# References: # [1] https://linuxhandbook.com/file-timestamps/ # [2] https://www.unixtutorial.org/atime-ctime-mtime-in-unix-filesystems/ #---------------------------------------------------------------------- @@ -31,7 +31,7 @@ def usage() : sys.exit() def copyright(): - print("\nProgram License: GPLv3\nMaintenance URL: https://github.com/E3V3A/pip-date") + print("\nProgram License: GPLv3\nMaintenance URL: https://github.com/E3V3A/pip-date") sys.exit() def pversion(): @@ -55,7 +55,7 @@ def print_legend(): #------------------------------------------------ # CLI arguments #------------------------------------------------ -narg = len(sys.argv) - 1 +narg = len(sys.argv) - 1 try: opts, args = getopt.getopt(sys.argv[1:], ":hvc", ["help", "version"]) @@ -63,9 +63,9 @@ except getopt.GetoptError : usage() sys.exit(2) -if not opts: - if not args or narg > 1: - usage(); +if not opts: + if not args or narg > 1: + usage(); sys.exit(); elif narg == 1: filename = args[0] @@ -73,7 +73,7 @@ else: for opt, arg in opts: if opt in ("-h", "--help"): usage(); elif opt in ("-v", "--version"): pversion(); - elif opt == "-c": copyright(); + elif opt == "-c": copyright(); #------------------------------------------------ # MAIN @@ -83,7 +83,7 @@ if platform.architecture()[1] == "WindowsPE": else: isWinFS = False -try: +try: fhand = open(filename) except IsADirectoryError as e: print ("ERROR: %s" % e) @@ -100,7 +100,7 @@ except UnicodeDecodeError as e: #print ("\nThis is a binary file.") is_binary = True t_char = 0 - pass + pass try: file_stats = os.stat(filename) @@ -127,9 +127,9 @@ print (" File Type : ", end='') if (stat.S_ISDIR(file_stats[stat.ST_MODE])): print (" directory") #sys.exit(2) -elif is_binary: +elif is_binary: print (" binary") -else: +else: print(" normal (text) file") print (" File Size : ", file_info['fsize'] , " (bytes)") @@ -145,7 +145,7 @@ if isWinFS: print (" ctime: OS change time : ", file_info['f_ct'], " (PS: n/a)") print (" mtime: user modified : ", file_info['f_lm'], " (PS: .LastWriteTime)") print (" atime: creation time : ", file_info['f_la'], " (PS: .CreationTime = .LastAccessTime)") -else: +else: print("Using a Linux based OS?") print (" ctime: creation time : ", file_info['f_ct']) print (" mtime: last modified : ", file_info['f_lm']) diff --git a/setup.py b/setup.py index 8a43890..65760c3 100644 --- a/setup.py +++ b/setup.py @@ -18,34 +18,39 @@ def readme(): setup( name = 'pip-date', - version = '1.0.3', + version = '1.0.4', author = 'E:V:A', author_email = 'xdae3v3a@gmail.com', description = 'Show the installation/modification times of all your pip packages and other tools', - long_description = 'A light CLI tool-set to show the installation or modification times of all your pip packages.', - long_description_content_type='text/plain', - #long_description = readme(), - #long_description_content_type = 'text/markdown', + #long_description = 'A light CLI tool-set to show the installation or modification times of all your pip packages.', + #long_description_content_type='text/plain', + long_description = readme(), + long_description_content_type = 'text/markdown', license='LICENSE.txt', url = 'https://github.com/E3V3A/pip-date/', packages = find_packages(), - scripts=['pip-date', 'pip-describe', 'pipbyday', 'pyfileinfo', 'pyOSinfo'], + scripts=['pip-date', 'pip-describe', 'pip-search.py', 'pipbyday', 'pyfileinfo', 'pyOSinfo'], keywords = 'pip date package management setuptools wheel egg stat os', install_requires=[ - 'requests', + 'requests', 'lxml', ], python_requires = '>=3', classifiers=[ #'Private :: Do Not Upload', 'Development Status :: 5 - Production/Stable', + 'Intended Audience :: Developers', 'License :: OSI Approved :: GNU General Public License v3 (GPLv3)', 'Operating System :: OS Independent', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', 'Topic :: Software Development :: Version Control', - 'Topic :: System :: Software Distribution', 'Topic :: System :: Installation/Setup', + 'Topic :: System :: Software Distribution', + 'Topic :: System :: Systems Administration', ], project_urls={ 'Bug Reports': 'https://github.com/E3V3A/pip-date/issues',