-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #61 from MODFLOW-USGS/v0.1.4
* ci(release): update to development version 0.1.4 * docs(fixtures): add docs for model filtering by name/package (#52) * docs(install): fix test model repo clone commands (#53) * refactor(has_pkg): use import.metadata instead of pkg_resources (#54) * Update docs (#55) * docs: minor updates to DEVELOPER.md and README.md * docs: add docs for download utilities * docs: update docs for download utilities (#56) * docs: expand docs for MFZipFile (#58) * chore: remove empty files, fix long description in setup.cfg (#59) * fix(fixtures): fix package detection/selection (#60) * ci(release): set version to 0.1.4, update changelog Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: w-bonelli <[email protected]> Co-authored-by: Mike Taves <[email protected]>
- Loading branch information
Showing
16 changed files
with
245 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,43 @@ | ||
# Downloads | ||
# Downloads | ||
|
||
Some utility functions are provided to query information and download artifacts and assets from the GitHub API. These are available in the `modflow_devtools.download` module. | ||
|
||
**Note:** to avoid GitHub API rate limits when using these functions, it is recommended to set the `GITHUB_TOKEN` environment variable. If this variable is set, the token will automatically be borne on requests sent to the API. | ||
|
||
## Retrieving information | ||
|
||
The following functions ask the GitHub API for information about a repository. The singular functions return a dictionary, while the plural functions return a list of dictionaries, with dictionary contents parsed directly from the API response's JSON. | ||
|
||
- `get_releases(repo, per_page=None, quiet=False)` | ||
- `get_release(repo, tag="latest", quiet=False)` | ||
- `list_release_assets(repo, tag="latest", quiet=False)` | ||
- `list_artifacts(repo, name, per_page=None, max_pages=None, quiet=False)` | ||
|
||
The `repo` parameter's format is `owner/name`, as in GitHub URLs. | ||
|
||
For instance, to download information about a release and inspect available assets: | ||
|
||
```python | ||
from modflow_devtools.download import get_release | ||
|
||
release = get_release("MODFLOW-USGS/executables") | ||
assets = release["assets"] | ||
expected_names = ["linux.zip", "mac.zip", "win64.zip"] | ||
actual_names = [asset["name"] for asset in assets] | ||
assert set(expected_names) == set(actual_names) | ||
``` | ||
|
||
## Downloading assets | ||
|
||
The `download_artifact(repo, id, path=None, delete_zip=True, quiet=False)` function downloads and unzips the GitHub Actions artifact with the given ID to the given path, optionally deleting the zipfile afterwards. The `repo` format is `owner/name`, as in GitHub URLs. | ||
|
||
The `download_and_unzip(url, path=None, delete_zip=True, verbose=False)` function is a more generic alternative for downloading and unzipping files from arbitrary URLs. | ||
|
||
For instance, to download a MODFLOW 6.3.0 Linux distribution and delete the zipfile after extracting: | ||
|
||
```python | ||
from modflow_devtools.download import download_and_unzip | ||
|
||
url = f"https://github.com/MODFLOW-USGS/modflow6/releases/download/6.3.0/mf6.3.0_linux.zip" | ||
download_and_unzip(url, "some/download/path", delete_zip=True, verbose=True) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
# `MFZipFile` | ||
|
||
Python's `ZipFile` doesn't preserve file permissions at extraction time. The `MFZipFile` subclass modifies `ZipFile.extract()` to do so, as per the recommendation [here](https://stackoverflow.com/questions/39296101/python-zipfile-removes-execute-permissions-from-binaries). | ||
Python's [`ZipFile`](https://docs.python.org/3/library/zipfile.html) doesn't [preserve file permissions at extraction time](https://bugs.python.org/issue15795). The `MFZipFile` subclass modifies `ZipFile.extract()` to do so, as per the recommendation [here](https://stackoverflow.com/questions/39296101/python-zipfile-removes-execute-permissions-from-binaries), and maintains identical behavior otherwise. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
__author__ = "Joseph D. Hughes" | ||
__date__ = "Jan 07, 2023" | ||
__version__ = "0.1.3" | ||
__date__ = "Jan 18, 2023" | ||
__version__ = "0.1.4" | ||
__maintainer__ = "Joseph D. Hughes" | ||
__email__ = "[email protected]" | ||
__status__ = "Production" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,9 @@ | ||
import importlib | ||
import socket | ||
import sys | ||
import traceback | ||
from contextlib import contextmanager | ||
from importlib import metadata | ||
from os import PathLike, chdir, environ, getcwd | ||
from os.path import basename, normpath | ||
from pathlib import Path | ||
|
@@ -10,7 +12,6 @@ | |
from typing import List, Optional, Tuple | ||
from urllib import request | ||
|
||
import pkg_resources | ||
from _warnings import warn | ||
|
||
|
||
|
@@ -118,20 +119,46 @@ def get_current_branch() -> str: | |
|
||
def get_packages(namefile_path: PathLike) -> List[str]: | ||
""" | ||
Return a list of packages used by the model defined in the given namefile. | ||
Return a list of packages used by the simulation or model defined in the given namefile. | ||
The namefile may be for an entire simulation or for a GWF or GWT model. If a simulation | ||
namefile is given, packages used in its component model namefiles will be included. | ||
Parameters | ||
---------- | ||
namefile_path : PathLike | ||
path to MODFLOW 6 name file | ||
path to MODFLOW 6 simulation or model name file | ||
Returns | ||
------- | ||
list of package types | ||
a list of packages used by the simulation or model | ||
""" | ||
with open(namefile_path, "r") as f: | ||
lines = f.readlines() | ||
|
||
ftypes = [] | ||
packages = [] | ||
path = Path(namefile_path).expanduser().absolute() | ||
lines = open(path, "r").readlines() | ||
gwf_lines = [l for l in lines if l.strip().lower().startswith("gwf6 ")] | ||
gwt_lines = [l for l in lines if l.strip().lower().startswith("gwt6 ")] | ||
|
||
def parse_model_namefile(line): | ||
nf_path = [path.parent / s for s in line.split(" ") if s != ""][1] | ||
if nf_path.suffix != ".nam": | ||
raise ValueError( | ||
f"Failed to parse GWF or GWT model namefile from simulation namefile line: {line}" | ||
) | ||
return nf_path | ||
|
||
# load model namefiles | ||
try: | ||
for line in gwf_lines: | ||
packages = ( | ||
packages + get_packages(parse_model_namefile(line)) + ["gwf"] | ||
) | ||
for line in gwt_lines: | ||
packages = ( | ||
packages + get_packages(parse_model_namefile(line)) + ["gwt"] | ||
) | ||
except: | ||
warn(f"Invalid namefile format: {traceback.format_exc()}") | ||
|
||
for line in lines: | ||
# Skip over blank and commented lines | ||
ll = line.strip().split() | ||
|
@@ -149,9 +176,9 @@ def get_packages(namefile_path: PathLike) -> List[str]: | |
# strip "6" from package name | ||
l = l.replace("6", "") | ||
|
||
ftypes.append(l.lower()) | ||
packages.append(l.lower()) | ||
|
||
return list(set(ftypes)) | ||
return list(set(packages)) | ||
|
||
|
||
def has_package(namefile_path: PathLike, package: str) -> bool: | ||
|
@@ -179,7 +206,7 @@ def get_namefile_paths( | |
if not Path(path).is_dir(): | ||
return [] | ||
|
||
# find namefiles | ||
# find simulation namefiles | ||
paths = [ | ||
p | ||
for p in Path(path).rglob( | ||
|
@@ -310,16 +337,14 @@ def has_pkg(pkg): | |
Originally written by Mike Toews ([email protected]) for FloPy. | ||
""" | ||
if pkg not in _has_pkg_cache: | ||
|
||
# for some dependencies, package name and import name are different | ||
# (e.g. pyshp/shapefile, mfpymake/pymake, python-dateutil/dateutil) | ||
# pkg_resources expects package name, importlib expects import name | ||
try: | ||
_has_pkg_cache[pkg] = bool(importlib.import_module(pkg)) | ||
except (ImportError, ModuleNotFoundError): | ||
try: | ||
_has_pkg_cache[pkg] = bool(pkg_resources.get_distribution(pkg)) | ||
except pkg_resources.DistributionNotFound: | ||
_has_pkg_cache[pkg] = False | ||
found = True | ||
try: # package name, e.g. pyshp | ||
metadata.distribution(pkg) | ||
except metadata.PackageNotFoundError: | ||
try: # import name, e.g. "import shapefile" | ||
importlib.import_module(pkg) | ||
except ModuleNotFoundError: | ||
found = False | ||
_has_pkg_cache[pkg] = found | ||
|
||
return _has_pkg_cache[pkg] |
Empty file.
Empty file.
Oops, something went wrong.