Skip to content

Commit 4e0d900

Browse files
committed
First version
0 parents  commit 4e0d900

File tree

11 files changed

+194
-0
lines changed

11 files changed

+194
-0
lines changed

.gitignore

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Virtual environment
2+
venv/
3+
4+
# PyCharm
5+
.idea/
6+
7+
# Cache
8+
__pycache__
9+
10+
# PyPi
11+
build/
12+
dist/
13+
*.egg-info

LICENSE

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2018 The Python Packaging Authority
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is
8+
furnished to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+
SOFTWARE.

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# PyPiSearch
2+
One more replacement of temporarily deprecated pip search command implemented on regular expressions and uses [pypi site](https://pypi.org/) search line.
3+
4+
## Install and run
5+
- Clone this repo and run `python pypisearch <query>`
6+
- Install it by pip `pip install pypisearch` and run `python -m pypisearch <query>`

pypisearch/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__version__ = "1.0.2"

pypisearch/__main__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
from pypisearch.main import main
2+
3+
4+
if __name__ == "__main__":
5+
exit(main())

pypisearch/main.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import argparse
2+
3+
from pypisearch.search import Search
4+
5+
6+
def main() -> None:
7+
"""Main program entrypoint."""
8+
9+
# Get command arguments
10+
arg_parser = argparse.ArgumentParser(
11+
description="Custom pip-search utility by pypi search line"
12+
)
13+
arg_parser.add_argument(
14+
"q",
15+
metavar="query", type=str, help="query for search"
16+
)
17+
arg_parser.add_argument(
18+
"-p", "--page",
19+
default=1, metavar="page", type=int, help="search page (default 1)"
20+
)
21+
args = arg_parser.parse_args()
22+
23+
# Parse search url and print result table
24+
search = Search(query=args.q, page=args.page)
25+
print(search.tabulated_result)
26+
27+
28+
if __name__ == "__main__":
29+
main()

pypisearch/re_constants.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import re
2+
3+
4+
DESCRIPTION_RE = re.compile(
5+
r".*<p class=\"package-snippet__description\">(.+)</p>"
6+
)
7+
ITEM_RE = re.compile(r"<a class=\"package-snippet\".*>")
8+
NAME_RE = re.compile(r"<span class=\"package-snippet__name\">(.+)</span>")
9+
VERSION_RE = re.compile(
10+
r".*<span class=\"package-snippet__version\">(.+)</span>"
11+
)

pypisearch/result_item.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import pypisearch.re_constants as const
2+
3+
4+
class ResultItem:
5+
"""Result item instance of search."""
6+
7+
def __init__(self, plain_text: str) -> None:
8+
self.plain_text = plain_text
9+
10+
@property
11+
def is_empty(self) -> bool:
12+
"""Check is current instance empty."""
13+
14+
return not all([self.name, self.version, self.description])
15+
16+
@property
17+
def name(self) -> str:
18+
"""Returns item name."""
19+
20+
name = const.NAME_RE.findall(self.plain_text)
21+
return name[0] if name else ""
22+
23+
@property
24+
def version(self) -> str:
25+
"""Returns item version."""
26+
27+
version = const.VERSION_RE.findall(self.plain_text)
28+
return version[0] if version else ""
29+
30+
@property
31+
def description(self) -> str:
32+
"""Returns item description."""
33+
34+
description = const.DESCRIPTION_RE.findall(self.plain_text)
35+
return description[0] if description else ""

pypisearch/search.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import requests
2+
3+
import tabulate
4+
5+
from pypisearch import re_constants as const
6+
from pypisearch.result_item import ResultItem
7+
8+
9+
class Search:
10+
"""Main search process instance."""
11+
12+
def __init__(self, query: str, page: int = 1) -> None:
13+
self.search_url = f"https://pypi.org/search/?q={query}&page={page}"
14+
15+
@property
16+
def get_page_data(self) -> str:
17+
"""Returns page's HTML code."""
18+
19+
return requests.get(url=self.search_url).text
20+
21+
@property
22+
def plain_items(self) -> list:
23+
"""Returns plain result items."""
24+
25+
return const.ITEM_RE.split(self.get_page_data)
26+
27+
@property
28+
def result(self) -> list:
29+
"""Returns list of result instances."""
30+
31+
return list(filter(
32+
lambda result_item: not result_item.is_empty,
33+
map(
34+
lambda plain_item: ResultItem(plain_text=plain_item),
35+
self.plain_items
36+
)
37+
))
38+
39+
@property
40+
def tabulated_result(self) -> str:
41+
"""Returns tabulated list of results."""
42+
43+
return tabulate.tabulate([
44+
[f"{item.name} ({item.version})", item.description]
45+
for item in self.result
46+
], tablefmt="plain") or "Sorry, we haven't results by your query"

requirements.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
certifi==2020.12.5
2+
chardet==4.0.0
3+
idna==2.10
4+
requests==2.25.1
5+
tabulate==0.8.7
6+
urllib3==1.26.2

0 commit comments

Comments
 (0)