Skip to content

Commit

Permalink
Merge pull request #8 from smsearcy/7-folder-with-version-error
Browse files Browse the repository at this point in the history
Correctly handle a tarball in which the root folder name does not match the Docset name.
  • Loading branch information
smsearcy authored Sep 13, 2024
2 parents 893bbea + 9b8fb9c commit c29e614
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 47 deletions.
7 changes: 5 additions & 2 deletions src/zeal_feeds/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
from rich.progress import Progress
from rich.spinner import Spinner

from zeal_feeds import ApplicationError, user_contrib, zeal
from zeal_feeds import ApplicationError, user_contrib
from zeal_feeds.zeal import Zeal


def main():
Expand Down Expand Up @@ -82,6 +83,8 @@ def install(args) -> str | None:
if missing_docsets:
return f"Failed to find the following docsets: {', '.join(missing_docsets)}"

zeal = Zeal.from_config()

installed_docsets = set(zeal.installed_docsets())
for docset in found_docsets.values():
if docset is None:
Expand All @@ -92,7 +95,7 @@ def install(args) -> str | None:
archive = _download_archive(docset)
spinner = Spinner("simpleDots", f"Installing {docset.name}")
with Live(spinner):
zeal.install(docset, archive)
zeal.install_docset(docset, archive)
archive.unlink()

return None
Expand Down
98 changes: 53 additions & 45 deletions src/zeal_feeds/zeal.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
from . import ApplicationError
from .user_contrib import DocSet

FEED_URL = "https://zealusercontributions.vercel.app/api/docsets/{name}.xml"


@attrs.define
class MetaData:
Expand All @@ -33,51 +35,57 @@ class MetaData:
urls: list[str]


FEED_URL = "https://zealusercontributions.vercel.app/api/docsets/{name}.xml"


@functools.cache
def docset_install_path() -> Path:
"""Get the path where Docsets are installed to."""
if sys.platform == "win32":
install_path = _windows_docset_install_path()
else:
install_path = _linux_docset_install_path()
if not install_path.exists():
install_path.mkdir(parents=True)
return install_path


def installed_docsets() -> Iterator[str]:
"""List of locally installed DocSets."""
docset_path = docset_install_path()

for meta_json in docset_path.glob("*/meta.json"):
metadata = json.load(meta_json.open("r"))
yield metadata["name"]


def install(docset: DocSet, tarball: Path):
"""Install a docset into the Zeal data directory."""
# TODO: don't install if docset already installed
docset_path = docset_install_path()

with tarfile.open(tarball) as docset_archive:
docset_folder = docset_archive.next()
if not (docset_folder and re.match(r"\w+\.docset", docset_folder.name)):
raise ApplicationError(f"Unexpected contents for {docset.name} archive")
docset_archive.extractall(docset_path)

converter = Converter()
meta_json = docset_path / docset_folder.name / "meta.json"
metadata = MetaData(
name=docset.name,
title=docset.title,
version=docset.version,
urls=docset.urls,
feed_url=FEED_URL.format(name=docset.name),
)
json.dump(converter.unstructure(metadata), meta_json.open("w"), indent=2)
@attrs.define
class Zeal:
docset_path: Path

@classmethod
def from_config(cls):
"""Get the path where Docsets are installed to."""
if sys.platform == "win32":
docset_path = _windows_docset_install_path()
else:
docset_path = _linux_docset_install_path()
if not docset_path.exists():
docset_path.mkdir(parents=True)
return cls(docset_path)

def installed_docsets(self) -> Iterator[str]:
"""List of locally installed DocSets."""
for meta_json in self.docset_path.glob("*/meta.json"):
metadata = json.load(meta_json.open("r"))
yield metadata["name"]

def install_docset(self, docset: DocSet, tarball: Path) -> None:
"""Install a docset into the Zeal data directory."""
# TODO: don't install if docset already installed
with tarfile.open(tarball) as docset_archive:
docset_folder = docset_archive.next()
if not (docset_folder and docset_folder.name.endswith(".docset")):
raise ApplicationError(f"Unexpected contents for {docset.name} archive")
docset_archive.extractall(self.docset_path)

# ensure docset folder given the correct name
expected_folder_name = f"{docset.name}.docset"
if docset_folder.name != expected_folder_name:
destination = self.docset_path / expected_folder_name
if destination.exists():
raise ApplicationError(
f"Destination folder already exists: {destination}"
)
source = self.docset_path / docset_folder.name
source.rename(destination)

converter = Converter()
meta_json = self.docset_path / expected_folder_name / "meta.json"
metadata = MetaData(
name=docset.name,
title=docset.title,
version=docset.version,
urls=docset.urls,
feed_url=FEED_URL.format(name=docset.name),
)
json.dump(converter.unstructure(metadata), meta_json.open("w"), indent=2)


def _linux_docset_install_path() -> Path:
Expand Down
Binary file added tests/data/wxPython.tgz
Binary file not shown.
27 changes: 27 additions & 0 deletions tests/test_zeal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
"""Test functionality in the `zeal` module."""

from zeal_feeds.zeal import Zeal
from zeal_feeds.user_contrib import DocSet, DocSetAuthor


def test_docset_install(data_folder, tmp_path) -> None:

docset_name = "wxPython"
docset_archive = "wxPython.tgz"

zeal = Zeal(tmp_path)
docset_tarball = data_folder / docset_archive

docset_meta = DocSet(
name=docset_name,
author=DocSetAuthor(
name="",
link="",
),
archive=docset_archive,
version="",
)

zeal.install_docset(docset_meta, docset_tarball)
print("Directory contents:", list(tmp_path.iterdir()))
assert (tmp_path / f"{docset_name}.docset").is_dir()

0 comments on commit c29e614

Please sign in to comment.