├── requirements.txt ├── src └── pypi_analyser │ ├── __pycache__ │ ├── __init__.cpython-39.pyc │ └── pypi_analyser.cpython-39.pyc │ ├── __init__.py │ └── pypi_analyser.py ├── CONTRIBUTING.md ├── test.py ├── setup.py ├── LICENSE └── README.md /requirements.txt: -------------------------------------------------------------------------------- 1 | beautifulsoup4==4.11.1 2 | requests==2.26.0 3 | setuptools==58.0.4 4 | -------------------------------------------------------------------------------- /src/pypi_analyser/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iampukar/pypi_analyser/main/src/pypi_analyser/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /src/pypi_analyser/__pycache__/pypi_analyser.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iampukar/pypi_analyser/main/src/pypi_analyser/__pycache__/pypi_analyser.cpython-39.pyc -------------------------------------------------------------------------------- /src/pypi_analyser/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | pypi-analyser 3 | 4 | A Python library to monitor PyPi packages! 5 | """ 6 | from . import pypi_analyser 7 | __version__ = "1.0.0" 8 | __author__ = 'Pukar Acharya' 9 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributing 3 | 4 | Thank you for your interest in contributing to pypi analyser! 5 | 6 | ## Pull requests 7 | 8 | **Please open all pull requests against the `main` branch.**. 9 | 10 | 11 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from src.pypi_analyser.pypi_analyser import pypi_analyser 3 | 4 | class Test(unittest.TestCase): 5 | def test_cases(self): 6 | """ 7 | Test Case1: babel-core 8 | """ 9 | package_name = 'pandas' 10 | package_details = pypi_analyser(package_name) 11 | 12 | self.assertEqual(package_details.package_name, 'pandas') 13 | self.assertEqual(package_details.released, 'Dec 25, 2009') 14 | self.assertEqual(package_details.author, 'The Pandas Development Team') 15 | self.assertEqual(package_details.homepage, 'https://pandas.pydata.org') 16 | 17 | if __name__ == '__main__': 18 | unittest.main() 19 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import setuptools 2 | 3 | with open("README.md", "r", encoding="utf-8") as fh: 4 | long_description = fh.read() 5 | 6 | setuptools.setup( 7 | name="pypi_analyser", 8 | version="1.0.0", 9 | author="Pukar Acharya", 10 | description="A Python library to monitor PyPi packages!", 11 | long_description=long_description, 12 | long_description_content_type="text/markdown", 13 | url="https://github.com/iampukar/pypi_analyser", 14 | project_urls={ 15 | "Bug Tracker": "https://github.com/iampukar/pypi_analyser/issues", 16 | }, 17 | classifiers=[ 18 | "Programming Language :: Python :: 3", 19 | "License :: OSI Approved :: MIT License", 20 | ], 21 | package_dir={"": "src"}, 22 | packages=setuptools.find_packages(where="src"), 23 | python_requires=">=3.9.7", 24 | ) 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Pukar Acharya 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | ----------------------------------------------------------------------------- 3 | 4 | pypi_analyser is a Python library to monitor PyPi packages! 5 | 6 | ## Package Installer 7 | 8 | pip install pypi-analyser 9 | 10 | ## Usage 11 | 12 | from pypi_analyser import pypi_analyser 13 | ''' 14 | package_name -> string pypi package name. 15 | ''' 16 | package_name = 'pandas' 17 | package_details = pypi_analyser(package_name) 18 | 19 | print(package_details.package_name) 20 | print(package_details.latest_version) 21 | print(package_details.license) 22 | print(package_details.homepage) 23 | print(package_details.stars) 24 | print(package_details.forks) 25 | print(package_details.dependency_count) 26 | print(package_details.dependencies) 27 | 28 | **Utilities** 29 | 30 | | Name | Description | 31 | | ------------- | -----| 32 | | package_name | Returns the pypi package name! | 33 | | description | Returns the description of pypi package! | 34 | | latest_version | Returns the latest version of pypi package! | 35 | | released | Returns the released date of the pypi package! | 36 | | latest_release | Returns the latest release date of pypi package! | 37 | | license | Returns the license of the pypi package! | 38 | | author | Returns the author of the pypi package! | 39 | | maintainer | Returns the maintainer of the pypi package! | 40 | | homepage | Returns the homepage of the pypi package! | 41 | | repository | Returns the repository of the pypi package! | 42 | | dependency_count | Returns the unique dependency count of the pypi package! | 43 | | dependencies | Returns the list of dependendencies for the pypi package! | 44 | | stars | Returns the github stars count of the pypi package! | 45 | | forks | Returns the github forks count of the pypi package! | 46 | 47 | 48 | ## Requirements 49 | 50 | The `requirements.txt` file has details of all Python libraries for this package, and can be installed using 51 | ``` 52 | pip install -r requirements.txt 53 | ``` 54 | 55 | ## Organization 56 | 57 | ├── src 58 | │   ├── pypi_analyser 59 |   ├── init <- init 60 | ├── pypi_analyser <- package source code for pypi analyser 61 | ├── setup.py <- setup file 62 | ├── LICENSE <- LICENSE 63 | ├── README.md <- README 64 | ├── CONTRIBUTING.md <- contribution 65 | ├── test.py <- test cases for unit testing 66 | ├── requirements.txt <- requirements file for reproducing the code package 67 | 68 | ## License 69 | 70 | MIT 71 | 72 | ## Contributions 73 | 74 | For steps on code contribution, please see [CONTRIBUTING](./CONTRIBUTING.md). 75 | -------------------------------------------------------------------------------- /src/pypi_analyser/pypi_analyser.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import warnings 3 | from bs4 import BeautifulSoup 4 | warnings.filterwarnings("ignore", category=UserWarning) 5 | 6 | class pypi_analyser: 7 | 8 | def __init__(self, package_name): 9 | self.package_name = package_name 10 | if self._get_package_sanity() == False: 11 | raise SystemExit('PyPi Package not found!') 12 | if self._get_package_sanity2() == False: 13 | raise SystemExit('Package not found at libraries.io!') 14 | 15 | self.description = self._description() 16 | self.latest_version = self._latest_version() 17 | self.released = self._released() 18 | self.latest_release = self._latest_release() 19 | self.license = self._license() 20 | self.author = self._author() 21 | self.maintainer = self._maintainer() 22 | 23 | self.homepage = self._homepage() 24 | self.repository = self._repository() 25 | self.stars = self._stars() 26 | self.forks = self._forks() 27 | self.dependency_count = self._dependency_count() 28 | self.dependencies = self._dependencies() 29 | 30 | def __setitem__(self, key, value): 31 | setattr(self, key, value) 32 | 33 | def __getitem__(self, key): 34 | return getattr(self, key) 35 | 36 | def __repr__(self): 37 | return "%s" % (self.url) 38 | 39 | def __str__(self): 40 | return "%s" % (self.url) 41 | 42 | def _get_package_sanity(self): 43 | package_status = requests.get(f"https://pypi.org/project/{self.package_name}/") 44 | return True if package_status.status_code == 200 else False 45 | 46 | def _get_package_sanity2(self): 47 | package_status = requests.get(f"https://libraries.io/pypi/{self.package_name}/") 48 | return True if package_status.status_code == 200 else False 49 | 50 | def _package_pypi_parcer(self): 51 | page = requests.get(f"https://pypi.org/project/{self.package_name}/") 52 | soup = BeautifulSoup(page.content, "html.parser") 53 | return soup 54 | 55 | def _package_libraries_parcer(self): 56 | page = requests.get(f"https://libraries.io/pypi/{self.package_name}/") 57 | soup = BeautifulSoup(page.content, "html.parser") 58 | return soup 59 | 60 | def pypi_json(self): 61 | return requests.get(f"https://pypi.org/pypi/{self.package_name}/json").json() 62 | 63 | def _description(self): 64 | soup = self._package_pypi_parcer() 65 | tag = soup.find("div", {"class":"package-description"}) 66 | return tag.findNext('p').get_text().strip() or None 67 | 68 | def _latest_version(self): 69 | soup = self._package_pypi_parcer() 70 | try: 71 | tag = soup.find("h1", {"class":"package-header__name"}).get_text().strip() 72 | return str(tag).split(' ')[1] 73 | except: 74 | return None 75 | 76 | def _license(self): 77 | try: 78 | json = self.pypi_json() 79 | return json['info']['license'] 80 | except: 81 | soup = self._package_pypi_parcer() 82 | tag = soup.find_all("p") 83 | for sub_tag in tag: 84 | content = sub_tag.get_text() 85 | if content.startswith('License'): 86 | return ' '.join(sub_tag.get_text().split(' ')[1:]) 87 | return None 88 | 89 | def _author(self): 90 | try: 91 | json = self.pypi_json() 92 | return json['info']['author'] 93 | except: 94 | soup = self._package_pypi_parcer() 95 | tag = soup.find_all("p") 96 | for sub_tag in tag: 97 | content = sub_tag.get_text() 98 | if content.startswith('Author'): 99 | return ' '.join(sub_tag.get_text().split(' ')[1:]) 100 | return None 101 | 102 | def _maintainer(self): 103 | soup = self._package_pypi_parcer() 104 | tag = soup.find_all("h3") 105 | for sub_tag in tag: 106 | content = sub_tag.get_text() 107 | if content.startswith('Maintainers'): 108 | return ''.join(sub_tag.findNext().get_text().strip()) 109 | 110 | def _latest_release(self): 111 | soup = self._package_pypi_parcer() 112 | tag = soup.find("p", {"class":"package-header__date"}) 113 | return tag.findNext('time').get_text().strip() 114 | 115 | def _homepage(self): 116 | try: 117 | json = self.pypi_json() 118 | return json['info']['project_urls']['Homepage'] 119 | except: 120 | soup = self._package_libraries_parcer() 121 | tag = soup.find("p", {"class":"project-links"}) 122 | if tag.findNext('a').get_text().startswith('Homepage'): 123 | return tag.findNext('a').get('href') 124 | 125 | def _repository(self): 126 | soup = self._package_libraries_parcer() 127 | tag = soup.find("p", {"class":"project-links"}) 128 | if tag.findNext('a').get_text().startswith('Repository'): 129 | return tag.findNext('a').get('href') 130 | 131 | def _stars(self): 132 | soup = self._package_libraries_parcer() 133 | tag = soup.find_all("dt") 134 | for sub_tag in tag: 135 | content = sub_tag.get_text() 136 | if content.strip().startswith('Stars'): 137 | return sub_tag.findNext().get_text().strip() 138 | 139 | def _forks(self): 140 | soup = self._package_libraries_parcer() 141 | tag = soup.find_all("dt") 142 | for sub_tag in tag: 143 | content = sub_tag.get_text() 144 | if content.strip().startswith('Forks'): 145 | return sub_tag.findNext().get_text().strip() 146 | 147 | def _released(self): 148 | soup = self._package_libraries_parcer() 149 | tag = soup.find_all("dt") 150 | for sub_tag in tag: 151 | content = sub_tag.get_text() 152 | if content.strip().startswith('First release'): 153 | return sub_tag.findNext().get_text().strip() 154 | 155 | def _dependency_count(self): 156 | soup = self._package_libraries_parcer() 157 | tag = soup.find_all("dt") 158 | for sub_tag in tag: 159 | content = sub_tag.get_text() 160 | if content.strip().startswith('Dependencies'): 161 | return sub_tag.findNext().get_text().strip() 162 | 163 | def _dependencies(self): 164 | json = self.pypi_json() 165 | try: 166 | depen_requires = json['info']['requires_dist'] 167 | p_name = [l.split(' ')[0] for l in ','.join(depen_requires).split(',')] 168 | p_ver = [str(l.split('(')[1]).split(')')[0] for l in ','.join(depen_requires).split(',')] 169 | return [i + j for i, j in zip(p_name, p_ver)] 170 | except: 171 | return None 172 | 173 | 174 | if __name__ == '__main__': 175 | pypi_analyser(package_name) --------------------------------------------------------------------------------