pyportion 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- portion/__init__.py +0 -0
- portion/__main__.py +4 -0
- portion/base/__init__.py +6 -0
- portion/base/command.py +13 -0
- portion/commands/__init__.py +14 -0
- portion/commands/add.py +11 -0
- portion/commands/new.py +31 -0
- portion/commands/template.py +41 -0
- portion/core/__init__.py +8 -0
- portion/core/logger.py +27 -0
- portion/core/parser.py +65 -0
- portion/portion.py +21 -0
- pyportion-0.0.1.dist-info/METADATA +30 -0
- pyportion-0.0.1.dist-info/RECORD +17 -0
- pyportion-0.0.1.dist-info/WHEEL +5 -0
- pyportion-0.0.1.dist-info/licenses/LICENSE +21 -0
- pyportion-0.0.1.dist-info/top_level.txt +1 -0
portion/__init__.py
ADDED
|
File without changes
|
portion/__main__.py
ADDED
portion/base/__init__.py
ADDED
portion/base/command.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from abc import ABC
|
|
2
|
+
from abc import abstractmethod
|
|
3
|
+
|
|
4
|
+
from portion.core import Logger
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class CommandBase(ABC):
|
|
8
|
+
def __init__(self, logger: Logger, **kwargs: dict) -> None:
|
|
9
|
+
self.logger = logger
|
|
10
|
+
|
|
11
|
+
@abstractmethod
|
|
12
|
+
def execute(self) -> None:
|
|
13
|
+
raise NotImplementedError()
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from typing import Dict
|
|
2
|
+
|
|
3
|
+
from portion.base import CommandBase
|
|
4
|
+
from .add import AddCommand
|
|
5
|
+
from .new import NewCommand
|
|
6
|
+
from .template import TemplateCommand
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_commands() -> Dict[str, type[CommandBase]]:
|
|
10
|
+
return {
|
|
11
|
+
"new": NewCommand,
|
|
12
|
+
"add": AddCommand,
|
|
13
|
+
"template": TemplateCommand,
|
|
14
|
+
}
|
portion/commands/add.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
from portion.base import CommandBase
|
|
2
|
+
from portion.core.logger import Logger
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class AddCommand(CommandBase):
|
|
6
|
+
def __init__(self, logger: Logger) -> None:
|
|
7
|
+
super().__init__(logger=logger)
|
|
8
|
+
|
|
9
|
+
def execute(self) -> None:
|
|
10
|
+
self.logger.pulse("Executing add command")
|
|
11
|
+
self.logger.info("Done")
|
portion/commands/new.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from portion.base import CommandBase
|
|
4
|
+
from portion.core.logger import Logger
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class NewCommand(CommandBase):
|
|
8
|
+
def __init__(self,
|
|
9
|
+
name: str,
|
|
10
|
+
template: str,
|
|
11
|
+
logger: Logger) -> None:
|
|
12
|
+
super().__init__(logger=logger)
|
|
13
|
+
self.name = name
|
|
14
|
+
self.template = template
|
|
15
|
+
|
|
16
|
+
def is_project_exist(self) -> bool:
|
|
17
|
+
return os.path.exists(self.name)
|
|
18
|
+
|
|
19
|
+
def create_project(self) -> None:
|
|
20
|
+
os.mkdir(self.name)
|
|
21
|
+
|
|
22
|
+
def execute(self) -> None:
|
|
23
|
+
self.logger.pulse("Executing create command")
|
|
24
|
+
if self.is_project_exist():
|
|
25
|
+
self.logger.error("The project is already exist")
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
self.create_project()
|
|
29
|
+
self.logger.pulse("Created the project folder")
|
|
30
|
+
|
|
31
|
+
self.logger.info(f"{self.name} project has been created successfully")
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from platformdirs import user_data_dir
|
|
4
|
+
from git import Repo
|
|
5
|
+
|
|
6
|
+
from portion.base import CommandBase
|
|
7
|
+
from portion.core.logger import Logger
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TemplateCommand(CommandBase):
|
|
11
|
+
def __init__(self,
|
|
12
|
+
template_command: str,
|
|
13
|
+
logger: Logger,
|
|
14
|
+
link: str | None = None) -> None:
|
|
15
|
+
super().__init__(logger=logger)
|
|
16
|
+
self.template_command = template_command
|
|
17
|
+
self.link = link
|
|
18
|
+
|
|
19
|
+
self._pyportion_path = os.path.join(user_data_dir(), "pyportion")
|
|
20
|
+
|
|
21
|
+
def create_pyportion_dir(self) -> None:
|
|
22
|
+
if not os.path.exists(self._pyportion_path):
|
|
23
|
+
os.mkdir(self._pyportion_path)
|
|
24
|
+
|
|
25
|
+
def download_command(self) -> None:
|
|
26
|
+
if not self.link:
|
|
27
|
+
raise ValueError("The given link is not valid")
|
|
28
|
+
|
|
29
|
+
repo_name = self.link.split("/")[-1]
|
|
30
|
+
repo_path = os.path.join(self._pyportion_path, repo_name)
|
|
31
|
+
Repo.clone_from(self.link, repo_path)
|
|
32
|
+
|
|
33
|
+
def execute(self) -> None:
|
|
34
|
+
self.logger.pulse("Executing template command")
|
|
35
|
+
|
|
36
|
+
self.create_pyportion_dir()
|
|
37
|
+
|
|
38
|
+
if self.template_command == "download":
|
|
39
|
+
self.download_command()
|
|
40
|
+
|
|
41
|
+
self.logger.info("Done")
|
portion/core/__init__.py
ADDED
portion/core/logger.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Logger:
|
|
9
|
+
def __init__(self, quiet: bool, verbose: bool) -> None:
|
|
10
|
+
self.quiet = quiet
|
|
11
|
+
self.verbose = verbose
|
|
12
|
+
self._start_time = time.monotonic()
|
|
13
|
+
self._end_time = time.monotonic()
|
|
14
|
+
|
|
15
|
+
def pulse(self, message: str) -> None:
|
|
16
|
+
if not self.quiet and self.verbose:
|
|
17
|
+
self._end_time = time.monotonic()
|
|
18
|
+
taken_time = self._end_time - self._start_time
|
|
19
|
+
logging.info(f"[Time Taken: {taken_time:.2f}s] {message}")
|
|
20
|
+
|
|
21
|
+
def error(self, message: str) -> None:
|
|
22
|
+
if not self.quiet:
|
|
23
|
+
logging.info("Error: " + message)
|
|
24
|
+
|
|
25
|
+
def info(self, message: str) -> None:
|
|
26
|
+
if not self.quiet:
|
|
27
|
+
logging.info(message)
|
portion/core/parser.py
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
from argparse import ArgumentParser
|
|
3
|
+
from argparse import _SubParsersAction
|
|
4
|
+
from argparse import Namespace
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Parser:
|
|
8
|
+
def __init__(self) -> None:
|
|
9
|
+
self.parser = argparse.ArgumentParser(description="Portion")
|
|
10
|
+
|
|
11
|
+
def _add_generic_arguments(self,
|
|
12
|
+
suparser: argparse.ArgumentParser) -> None:
|
|
13
|
+
suparser.add_argument("-q",
|
|
14
|
+
default=False,
|
|
15
|
+
help="Suppress Logging",
|
|
16
|
+
action="store_true")
|
|
17
|
+
|
|
18
|
+
suparser.add_argument("-v",
|
|
19
|
+
default=False,
|
|
20
|
+
help="Verbose Logging",
|
|
21
|
+
action="store_true")
|
|
22
|
+
|
|
23
|
+
def _new_arguments(self, parser: _SubParsersAction) -> ArgumentParser:
|
|
24
|
+
new: ArgumentParser = parser.add_parser("new",
|
|
25
|
+
help="Create a new project")
|
|
26
|
+
new.add_argument("name")
|
|
27
|
+
new.add_argument("-t", "--template", required=True)
|
|
28
|
+
return new
|
|
29
|
+
|
|
30
|
+
def _add_arguments(self, parser: _SubParsersAction) -> ArgumentParser:
|
|
31
|
+
add = parser.add_parser("add", help="Add a portion")
|
|
32
|
+
return add
|
|
33
|
+
|
|
34
|
+
def _template_arguments(self, parser: _SubParsersAction) -> ArgumentParser:
|
|
35
|
+
template: ArgumentParser = parser.add_parser("template",
|
|
36
|
+
help="Manage templates")
|
|
37
|
+
template_subparser = template.add_subparsers(
|
|
38
|
+
dest="template_command",
|
|
39
|
+
help="Allows you to manage templates")
|
|
40
|
+
|
|
41
|
+
download_parser = template_subparser.add_parser(
|
|
42
|
+
"download", help="Download a template")
|
|
43
|
+
|
|
44
|
+
download_parser.add_argument("link")
|
|
45
|
+
|
|
46
|
+
# list_parser = template_subparser.add_parser(
|
|
47
|
+
# "list", help="List all templates")
|
|
48
|
+
|
|
49
|
+
# remove_parser = template_subparser.add_parser(
|
|
50
|
+
# "remove", help="Remove a template")
|
|
51
|
+
|
|
52
|
+
return template
|
|
53
|
+
|
|
54
|
+
def parse(self) -> Namespace:
|
|
55
|
+
subparser = self.parser.add_subparsers(dest="command", required=True)
|
|
56
|
+
argument_parsers = [
|
|
57
|
+
self._new_arguments(subparser),
|
|
58
|
+
self._template_arguments(subparser),
|
|
59
|
+
# self._add_arguments(subparser),
|
|
60
|
+
]
|
|
61
|
+
|
|
62
|
+
for argument_parser in argument_parsers:
|
|
63
|
+
self._add_generic_arguments(argument_parser)
|
|
64
|
+
|
|
65
|
+
return self.parser.parse_args()
|
portion/portion.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from portion.core import Parser
|
|
2
|
+
from portion.core import Logger
|
|
3
|
+
from portion.commands import get_commands
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Portion:
|
|
7
|
+
def __init__(self) -> None:
|
|
8
|
+
parser = Parser()
|
|
9
|
+
self.args = parser.parse()
|
|
10
|
+
self.commands = get_commands()
|
|
11
|
+
self.logger = Logger(self.args.q, self.args.v)
|
|
12
|
+
|
|
13
|
+
def _run_command(self) -> None:
|
|
14
|
+
command = self.commands.get(self.args.command, None)
|
|
15
|
+
if command:
|
|
16
|
+
params = command.__init__.__code__.co_varnames[1:]
|
|
17
|
+
kwargs = {k: v for k, v in vars(self.args).items() if k in params}
|
|
18
|
+
command(logger=self.logger, **kwargs).execute()
|
|
19
|
+
|
|
20
|
+
def run(self) -> None:
|
|
21
|
+
self._run_command()
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyportion
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Python CLI to manage your projects
|
|
5
|
+
Home-page: https://github.com/murtadapy/
|
|
6
|
+
Author: Murtada Altarouti
|
|
7
|
+
Author-email: murtada.altarouti@gmail.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/pyportion/pyportion/issues
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Dynamic: license-file
|
|
13
|
+
|
|
14
|
+
# PyPortion
|
|
15
|
+
|
|
16
|
+
PyPortion CLI helps you kickstart new projects and manage existing ones with ease. It streamlines project setup, enforces a clean and scalable structure, and lets you focus on delivering high-quality applications
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
* 🚀 Quick start – Scaffold new projects in seconds
|
|
20
|
+
* 🛠️ Project management – Easily maintain and organize existing projects
|
|
21
|
+
* 📦 Scalable structure – Enforces a clean, maintainable architecture
|
|
22
|
+
* 🔄 Consistency – Keeps your project structure up to standard
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
## Get started
|
|
26
|
+
|
|
27
|
+
### Install pyportion
|
|
28
|
+
```bash
|
|
29
|
+
pip install pyportion
|
|
30
|
+
```
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
portion/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
portion/__main__.py,sha256=DLtsZ0JmCg0aL1CYVRaQIVz8PEMRWzonSGv6tKvDUQk,84
|
|
3
|
+
portion/portion.py,sha256=ltCNQ0HLL5F1VHCOYD3Ljl2yQI9JQ2FnUbTx2kgeDSo,686
|
|
4
|
+
portion/base/__init__.py,sha256=55J-CVTc7EcB7QF0s2wtv9sLjaqJJTFNgEb7EKMYKXo,68
|
|
5
|
+
portion/base/command.py,sha256=KfhhOvved_wWStPyn2t5aEJxwtkNlWRGEBwEXnNwRo0,291
|
|
6
|
+
portion/commands/__init__.py,sha256=Al0gncqQmgwAqXgYZ4c3GRucn1Ul3i2K9usHHsmlLbg,320
|
|
7
|
+
portion/commands/add.py,sha256=a2tHqGB40Bg6iEVZKv6Er3yNIxDHyBwSdTDfS0RmcUU,313
|
|
8
|
+
portion/commands/new.py,sha256=pyLfisMUVLOqsGKX72yG8Cdrdfl-em8qHUk0oL0V15Q,867
|
|
9
|
+
portion/commands/template.py,sha256=TdEteeQd1GVcHrjFuHVN4qcZk2z8eTMk2_wciL71qt0,1192
|
|
10
|
+
portion/core/__init__.py,sha256=q1-p5vyZByJ7yqJDSbrvaAguOssBAUb3w0T99ZGEOhY,98
|
|
11
|
+
portion/core/logger.py,sha256=Y8cO8ldqIYJ6ZIq3Rq3L3ntnVkC4Mr_DFe0GeW3t9Oo,796
|
|
12
|
+
portion/core/parser.py,sha256=KuQGAg4_rc3c_2th5rOgVDjcRVw1IeVBGXDoe0pOO5Q,2401
|
|
13
|
+
pyportion-0.0.1.dist-info/licenses/LICENSE,sha256=p7CrR0Lwii4EWl8asEu41IMRIy5YQaa5wCLWunArnQs,1064
|
|
14
|
+
pyportion-0.0.1.dist-info/METADATA,sha256=_QTCyw4LSrUOFjxHKC5khP9cRBjACvi_Ys5hcjraXDM,966
|
|
15
|
+
pyportion-0.0.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
16
|
+
pyportion-0.0.1.dist-info/top_level.txt,sha256=08-yis9FueTZONmtrCF9k8pR9EDuyWJ5gzjtHD-Ad1A,8
|
|
17
|
+
pyportion-0.0.1.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Portion
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
portion
|