Skip to content

Commit

Permalink
feat(search): use tabular output and incl. source
Browse files Browse the repository at this point in the history
This change converts the output of the `search` command to use a table
instead of unstructured output. Additionally, the output now also
includes source name.
  • Loading branch information
abn committed Jan 6, 2025
1 parent be80205 commit b93cb20
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 98 deletions.
47 changes: 32 additions & 15 deletions src/poetry/console/commands/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,20 +23,37 @@ class SearchCommand(Command):
def handle(self) -> int:
seen = set()

for result in self.poetry.pool.search(self.argument("tokens")):
if result.pretty_string in seen:
continue

seen.add(result.pretty_string)

self.line("")
name = f"<info>{result.name}</>"

name += f" (<comment>{result.version}</>)"

self.line(name)

if result.description:
self.line(f" {result.description}")
table = self.table(style="compact")
table.set_headers(
["<b>Package</>", "<b>Version</>", "<b>Source</>", "<b>Description</>"]
)

rows = []

for repository in self.poetry.pool.repositories:
for result in repository.search(self.argument("tokens")):
key = f"{repository.name}::{result.pretty_string}"
if key in seen:
continue
seen.add(key)
rows.append((result, repository.name))

if not rows:
self.line("<info>No matching packages were found.</>")
return 0

for package, source in sorted(
rows, key=lambda x: (x[0].name, x[0].version, x[1])
):
table.add_row(
[
f"<c1>{package.name}</>",
f"<b>{package.version}</b>",
f"<fg=yellow;options=bold>{source}</>",
str(package.description),
]
)

table.render()

return 0
143 changes: 60 additions & 83 deletions tests/console/commands/test_search.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

import re

from pathlib import Path
from typing import TYPE_CHECKING

Expand All @@ -21,67 +23,28 @@
FIXTURES_DIRECTORY = (
TESTS_DIRECTORY / "repositories" / "fixtures" / "pypi.org" / "search"
)
SQLALCHEMY_SEARCH_OUTPUT_PYPI = """
sqlalchemy (1.3.10)
Database Abstraction Library
sqlalchemy-dao (1.3.1)
Simple wrapper for sqlalchemy.
graphene-sqlalchemy (2.2.2)
Graphene SQLAlchemy integration
sqlalchemy-utcdatetime (1.0.4)
Convert to/from timezone aware datetimes when storing in a DBMS
paginate-sqlalchemy (0.3.0)
Extension to paginate.Page that supports SQLAlchemy queries
sqlalchemy-audit (0.1.0)
sqlalchemy-audit provides an easy way to set up revision tracking for your data.
transmogrify-sqlalchemy (1.0.2)
Feed data from SQLAlchemy into a transmogrifier pipeline
sqlalchemy-schemadisplay (1.3)
Turn SQLAlchemy DB Model into a graph
sqlalchemy-traversal (0.5.2)
UNKNOWN
sqlalchemy-filters (0.10.0)
A library to filter SQLAlchemy queries.
sqlalchemy-wrap (2.1.7)
Python wrapper for the CircleCI API
sqlalchemy-nav (0.0.2)
SQLAlchemy-Nav provides SQLAlchemy Mixins for creating navigation bars compatible with\
Bootstrap
sqlalchemy-repr (0.0.1)
Automatically generates pretty repr of a SQLAlchemy model.
sqlalchemy-diff (0.1.3)
Compare two database schemas using sqlalchemy.
sqlalchemy-equivalence (0.1.1)
Provides natural equivalence support for SQLAlchemy declarative models.
broadway-sqlalchemy (0.0.1)
A broadway extension wrapping Flask-SQLAlchemy
jsonql-sqlalchemy (1.0.1)
Simple JSON-Based CRUD Query Language for SQLAlchemy
sqlalchemy-plus (0.2.0)
Create Views and Materialized Views with SqlAlchemy
cherrypy-sqlalchemy (0.5.3)
Use SQLAlchemy with CherryPy
sqlalchemy-sqlany (1.0.3)
SAP Sybase SQL Anywhere dialect for SQLAlchemy
SQLALCHEMY_SEARCH_OUTPUT_PYPI = """\
Package Version Source Description
broadway-sqlalchemy 0.0.1 PyPI A broadway extension wrapping Flask-SQLAlchemy
cherrypy-sqlalchemy 0.5.3 PyPI Use SQLAlchemy with CherryPy
graphene-sqlalchemy 2.2.2 PyPI Graphene SQLAlchemy integration
jsonql-sqlalchemy 1.0.1 PyPI Simple JSON-Based CRUD Query Language for SQLAlchemy
paginate-sqlalchemy 0.3.0 PyPI Extension to paginate.Page that supports SQLAlchemy queries
sqlalchemy 1.3.10 PyPI Database Abstraction Library
sqlalchemy-audit 0.1.0 PyPI sqlalchemy-audit provides an easy way to set up revision tracking for your data.
sqlalchemy-dao 1.3.1 PyPI Simple wrapper for sqlalchemy.
sqlalchemy-diff 0.1.3 PyPI Compare two database schemas using sqlalchemy.
sqlalchemy-equivalence 0.1.1 PyPI Provides natural equivalence support for SQLAlchemy declarative models.
sqlalchemy-filters 0.10.0 PyPI A library to filter SQLAlchemy queries.
sqlalchemy-nav 0.0.2 PyPI SQLAlchemy-Nav provides SQLAlchemy Mixins for creating navigation bars compatible with Bootstrap
sqlalchemy-plus 0.2.0 PyPI Create Views and Materialized Views with SqlAlchemy
sqlalchemy-repr 0.0.1 PyPI Automatically generates pretty repr of a SQLAlchemy model.
sqlalchemy-schemadisplay 1.3 PyPI Turn SQLAlchemy DB Model into a graph
sqlalchemy-sqlany 1.0.3 PyPI SAP Sybase SQL Anywhere dialect for SQLAlchemy
sqlalchemy-traversal 0.5.2 PyPI UNKNOWN
sqlalchemy-utcdatetime 1.0.4 PyPI Convert to/from timezone aware datetimes when storing in a DBMS
sqlalchemy-wrap 2.1.7 PyPI Python wrapper for the CircleCI API
transmogrify-sqlalchemy 1.0.2 PyPI Feed data from SQLAlchemy into a transmogrifier pipeline
"""


Expand All @@ -90,6 +53,10 @@ def tester(command_tester_factory: CommandTesterFactory) -> CommandTester:
return command_tester_factory("search")


def clean_output(text: str) -> str:
return re.sub(r"\s+\n", "\n", text)


def test_search(
tester: CommandTester, http: type[httpretty.httpretty], poetry: Poetry
) -> None:
Expand All @@ -98,11 +65,25 @@ def test_search(

tester.execute("sqlalchemy")

output = tester.io.fetch_output()
output = clean_output(tester.io.fetch_output())

assert output == SQLALCHEMY_SEARCH_OUTPUT_PYPI


def test_search_empty_results(
tester: CommandTester,
http: type[httpretty.httpretty],
poetry: Poetry,
legacy_repository: LegacyRepository,
) -> None:
poetry.pool.add_repository(legacy_repository)

tester.execute("does-not-exist")

output = tester.io.fetch_output()
assert output.strip() == "No matching packages were found."


def test_search_with_legacy_repository(
tester: CommandTester,
http: type[httpretty.httpretty],
Expand All @@ -114,14 +95,13 @@ def test_search_with_legacy_repository(

tester.execute("sqlalchemy")

expected = (
SQLALCHEMY_SEARCH_OUTPUT_PYPI
+ """
sqlalchemy-legacy (4.3.4)
"""
line_before = " sqlalchemy-filters 0.10.0 PyPI A library to filter SQLAlchemy queries."
additional_line = " sqlalchemy-legacy 4.3.4 legacy"
expected = SQLALCHEMY_SEARCH_OUTPUT_PYPI.replace(
line_before, f"{line_before}\n{additional_line}"
)

output = tester.io.fetch_output()
output = clean_output(tester.io.fetch_output())

assert output == expected

Expand All @@ -136,14 +116,13 @@ def test_search_only_legacy_repository(

tester.execute("ipython")

expected = """
ipython (5.7.0)
ipython (7.5.0)
expected = """\
Package Version Source Description
ipython 5.7.0 legacy
ipython 7.5.0 legacy
"""

output = tester.io.fetch_output()

output = clean_output(tester.io.fetch_output())
assert output == expected


Expand All @@ -157,17 +136,15 @@ def test_search_multiple_queries(

tester.execute("ipython isort")

expected = """
ipython (5.7.0)
ipython (7.5.0)
isort (4.3.4)
isort-metadata (4.3.4)
expected = """\
Package Version Source Description
ipython 5.7.0 legacy
ipython 7.5.0 legacy
isort 4.3.4 legacy
isort-metadata 4.3.4 legacy
"""

output = tester.io.fetch_output()
output = clean_output(tester.io.fetch_output())

# we use a set here to avoid ordering issues
assert set(output.split("\n")) == set(expected.split("\n"))

0 comments on commit b93cb20

Please sign in to comment.