-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
1,236 additions
and
6 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
"""cosmofy: Cosmopolitan Python Bundler""" | ||
|
||
# std | ||
from __future__ import annotations | ||
from typing import List | ||
from typing import Optional | ||
import logging | ||
import sys | ||
|
||
# pkg | ||
from .args import Args | ||
from .args import USAGE | ||
from .bundler import Bundler | ||
|
||
__version__ = "0.1.0" | ||
__pubdate__ = "unpublished" | ||
|
||
log_normal = "%(levelname)s: %(message)s" | ||
log_debug = "%(name)s.%(funcName)s: %(levelname)s: %(message)s" | ||
log_verbose = " %(filename)s:%(lineno)s %(funcName)s(): %(levelname)s: %(message)s" | ||
logging.basicConfig(level=logging.INFO, format=log_normal) | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
|
||
def main(argv: Optional[List[str]] = None) -> int: | ||
"""Main entry point.""" | ||
short_usage = "\n" + USAGE[USAGE.find("USAGE") + 5 : USAGE.find("GENERAL")].strip() | ||
|
||
try: | ||
args = Args.parse((argv or sys.argv)[1:]) | ||
except ValueError as e: | ||
log.error(e) | ||
print(short_usage) | ||
return 1 | ||
|
||
if args.debug: | ||
log.setLevel(logging.DEBUG) | ||
formatter = logging.Formatter(log_debug) | ||
for handler in logging.getLogger().handlers: | ||
handler.setFormatter(formatter) | ||
log.debug(args) | ||
|
||
if args.version: | ||
print(f"{__version__} ({__pubdate__})", flush=True) | ||
return 0 | ||
|
||
if args.help: | ||
print(USAGE) | ||
return 0 | ||
|
||
if not args.add: | ||
log.error("You must specify at least one path to add.") | ||
print(short_usage) | ||
return 1 | ||
|
||
if args.clone and not args.cosmo: | ||
log.error( | ||
"You cannot use --clone outside of a Cosmopolitan build. " | ||
"See https://github.com/metaist/cosmofy#install" | ||
) | ||
return 1 | ||
|
||
# output = Bundler(args).run() | ||
# TODO: generate json receipt if requested | ||
Bundler(args).run() | ||
return 0 | ||
|
||
|
||
if __name__ == "__main__": | ||
sys.exit(main()) |
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,2 +1,8 @@ | ||
#!/usr/bin/env python | ||
"""cosmofy: Cosmopolitan Python Bundler""" | ||
"""Main entry point.""" | ||
|
||
# no cover: start | ||
from . import main | ||
|
||
if __name__ == "__main__": | ||
main() | ||
# no cover: stop |
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 |
---|---|---|
@@ -0,0 +1,203 @@ | ||
"""Command-line arguments.""" | ||
|
||
# std | ||
from __future__ import annotations | ||
from os import environ as ENV | ||
from pathlib import Path | ||
from typing import List | ||
from typing import Optional | ||
import dataclasses | ||
import logging | ||
|
||
log = logging.getLogger(__name__) | ||
|
||
COSMOFY_PYTHON_URL = ENV.get( | ||
"COSMOFY_PYTHON_URL", "https://cosmo.zip/pub/cosmos/bin/python" | ||
) | ||
"""URL to download python from.""" | ||
|
||
COSMOFY_CACHE_DIR = Path( | ||
ENV.get("COSMOFY_CACHE_DIR", Path.home() / ".cache" / "cosmofy") | ||
) | ||
"""Path to cache directory.""" | ||
|
||
USAGE = f"""cosmofy: Cosmopolitan Python Bundler | ||
USAGE | ||
cosmofy | ||
[--help] [--version] [--debug] [--dry-run] | ||
[--python-url URL] [--cache PATH] [--clone] | ||
[--output PATH] [--args STRING] | ||
<add>... [--exclude GLOB]... [--remove GLOB]... | ||
GENERAL | ||
-h, --help | ||
Show this help message and exit. | ||
--version | ||
Show program version and exit. | ||
--debug | ||
Show debug messages. | ||
-n, --dry-run | ||
Do not make any file system changes. | ||
CACHE | ||
--python-url URL | ||
URL from which to download Cosmopolitan Python. | ||
[env: COSMOFY_PYTHON_URL={COSMOFY_PYTHON_URL}] | ||
--cache PATH | ||
Directory in which to cache Cosmopolitan Python downloads. | ||
Use `false` or `0` to disable caching. | ||
[env: COSMOFY_CACHE_DIR={COSMOFY_CACHE_DIR}] | ||
--clone | ||
EXPERIMENTAL: Whether to obtain python by cloning `cosmofy` and | ||
removing itself instead of downloading it from `--python-url` | ||
FILES | ||
-o PATH, --output PATH | ||
Path to output file (see below for default value). | ||
--args STRING | ||
Cosmopolitan Python arguments (see below for default value). | ||
--add GLOB, <add> | ||
At least one glob-like patterns to add. Folders are recursively added. | ||
Files ending in `.py` will be compiled. | ||
-x GLOB, --exclude GLOB | ||
One or more glob-like patterns to exclude from being added. | ||
By default, "*.egg-info" and "__pycache__" are excluded. | ||
--rm GLOB, --remove GLOB | ||
One or more glob-like patters to remove from the output. | ||
Common things to remove are `pip`, terminal info, and SSL certs: | ||
$ cosmofy src/my_module --rm 'usr/*' --rm 'Lib/site-packages/pip/*' | ||
NOTES | ||
When `--args` or `--output` is missing: | ||
- If `<path>` is a single file: | ||
--args = "-m <path_without_suffix>" | ||
--output = "<path_without_suffix>.com" | ||
- If `<path>` contains a `__main__.py`, the first one encountered: | ||
--args = "-m <parent_folder>" | ||
--output = "<parent_folder>.com" | ||
- If `<path>` contains a `__init__.py`, we search for the first file | ||
that contains the line `if __name__ == '__main__'`: | ||
--args = "-m <file_without_suffix>" | ||
--output = "<file_without_suffix>.com" | ||
""" | ||
|
||
|
||
@dataclasses.dataclass | ||
class Args: | ||
help: bool = False | ||
"""Whether to show usage.""" | ||
|
||
version: bool = False | ||
"""Whether to show version.""" | ||
|
||
debug: bool = False | ||
"""Whether to show debug messages.""" | ||
|
||
cosmo: bool = False | ||
"""Whether we are running inside a Cosmopolitan build.""" | ||
|
||
dry_run: bool = False | ||
"""Whether we should suppress any file-system operations.""" | ||
|
||
for_real: bool = True | ||
"""Internal value for the opposite of `dry_run`.""" | ||
|
||
# cache | ||
|
||
python_url: str = COSMOFY_PYTHON_URL | ||
"""URL from which to download Cosmopolitan Python.""" | ||
|
||
cache: Optional[Path] = COSMOFY_CACHE_DIR | ||
"""Directory for caching downloads.""" | ||
|
||
clone: bool = False | ||
"""Whether to clone `cosmofy` to get python.""" | ||
|
||
# files | ||
|
||
args: str = "" | ||
"""Args to pass to Cosmopolitan python.""" | ||
|
||
output: Optional[Path] = None | ||
"""Path to the output file.""" | ||
|
||
paths: List[Path] = dataclasses.field(default_factory=list) | ||
"""Paths to add.""" | ||
|
||
add: List[str] = dataclasses.field(default_factory=list) | ||
"""Globs to add.""" | ||
|
||
exclude: List[str] = dataclasses.field(default_factory=list) | ||
"""Globs to exclude.""" | ||
|
||
remove: List[str] = dataclasses.field(default_factory=list) | ||
"""Globs to remove.""" | ||
|
||
@staticmethod | ||
def parse(argv: List[str]) -> Args: | ||
args = Args() | ||
alias = { | ||
"-h": "--help", | ||
"-n": "--dry-run", | ||
"-o": "--output", | ||
"-x": "--exclude", | ||
"--rm": "--remove", | ||
} | ||
while argv: | ||
if argv[0].startswith("-"): | ||
arg = argv.pop(0) | ||
arg = alias.get(arg, arg) | ||
else: | ||
arg = "--add" | ||
prop = arg[2:].replace("-", "_") | ||
|
||
# bool | ||
if arg in [ | ||
"--clone", | ||
"--cosmo", | ||
"--debug", | ||
"--dry-run", | ||
"--help", | ||
"--version", | ||
]: | ||
setattr(args, prop, True) | ||
|
||
# str | ||
elif arg in ["--args", "--python-url"]: | ||
setattr(args, prop, argv.pop(0)) | ||
|
||
# path | ||
elif arg in ["--cache", "--output"]: | ||
setattr(args, prop, Path(argv.pop(0))) | ||
|
||
# list[str] | ||
elif arg in ["--add", "--exclude", "--remove"]: | ||
getattr(args, prop).append(argv.pop(0)) | ||
|
||
# unknown | ||
else: | ||
raise ValueError(f"Unknown argument: {arg}") | ||
|
||
args.for_real = not args.dry_run | ||
if args.cache and args.cache.name.lower() in ["0", "false"]: | ||
args.cache = None | ||
return args |
Oops, something went wrong.