pythonanywhere-core 0.1.8__tar.gz → 0.1.10__tar.gz
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.
- {pythonanywhere_core-0.1.8 → pythonanywhere_core-0.1.10}/PKG-INFO +25 -2
- pythonanywhere_core-0.1.10/README.rst +28 -0
- {pythonanywhere_core-0.1.8 → pythonanywhere_core-0.1.10}/pyproject.toml +5 -1
- pythonanywhere_core-0.1.10/pythonanywhere_core/__init__.py +1 -0
- {pythonanywhere_core-0.1.8 → pythonanywhere_core-0.1.10}/pythonanywhere_core/base.py +4 -2
- {pythonanywhere_core-0.1.8 → pythonanywhere_core-0.1.10}/pythonanywhere_core/files.py +17 -9
- {pythonanywhere_core-0.1.8 → pythonanywhere_core-0.1.10}/pythonanywhere_core/schedule.py +2 -2
- {pythonanywhere_core-0.1.8 → pythonanywhere_core-0.1.10}/pythonanywhere_core/students.py +1 -1
- {pythonanywhere_core-0.1.8 → pythonanywhere_core-0.1.10}/pythonanywhere_core/webapp.py +2 -3
- pythonanywhere_core-0.1.10/pythonanywhere_core/website.py +82 -0
- pythonanywhere_core-0.1.8/README.rst +0 -5
- pythonanywhere_core-0.1.8/pythonanywhere_core/__init__.py +0 -1
- {pythonanywhere_core-0.1.8 → pythonanywhere_core-0.1.10}/LICENSE +0 -0
- {pythonanywhere_core-0.1.8 → pythonanywhere_core-0.1.10}/pythonanywhere_core/exceptions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pythonanywhere-core
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.10
|
|
4
4
|
Summary: API wrapper for programmatic management of PythonAnywhere services.
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: pythonanywhere,api,cloud,web hosting
|
|
@@ -25,7 +25,30 @@ Description-Content-Type: text/x-rst
|
|
|
25
25
|
|
|
26
26
|
API wrapper for programmatic management of PythonAnywhere services.
|
|
27
27
|
|
|
28
|
-
It's
|
|
28
|
+
It's a core code behind `PythonAnywhere cli tool`_.
|
|
29
29
|
|
|
30
30
|
.. _PythonAnywhere cli tool: https://pypi.org/project/pythonanywhere/
|
|
31
31
|
|
|
32
|
+
|
|
33
|
+
Development
|
|
34
|
+
===========
|
|
35
|
+
|
|
36
|
+
To create local development environment, run:
|
|
37
|
+
|
|
38
|
+
.. code-block:: shell
|
|
39
|
+
|
|
40
|
+
poetry install
|
|
41
|
+
|
|
42
|
+
To run tests:
|
|
43
|
+
|
|
44
|
+
.. code-block:: shell
|
|
45
|
+
|
|
46
|
+
poetry run pytest
|
|
47
|
+
|
|
48
|
+
To build docs:
|
|
49
|
+
|
|
50
|
+
.. code-block:: shell
|
|
51
|
+
|
|
52
|
+
cd docs
|
|
53
|
+
poetry run sphinx-build -b html . _build
|
|
54
|
+
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
API wrapper for programmatic management of PythonAnywhere services.
|
|
2
|
+
|
|
3
|
+
It's a core code behind `PythonAnywhere cli tool`_.
|
|
4
|
+
|
|
5
|
+
.. _PythonAnywhere cli tool: https://pypi.org/project/pythonanywhere/
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
Development
|
|
9
|
+
===========
|
|
10
|
+
|
|
11
|
+
To create local development environment, run:
|
|
12
|
+
|
|
13
|
+
.. code-block:: shell
|
|
14
|
+
|
|
15
|
+
poetry install
|
|
16
|
+
|
|
17
|
+
To run tests:
|
|
18
|
+
|
|
19
|
+
.. code-block:: shell
|
|
20
|
+
|
|
21
|
+
poetry run pytest
|
|
22
|
+
|
|
23
|
+
To build docs:
|
|
24
|
+
|
|
25
|
+
.. code-block:: shell
|
|
26
|
+
|
|
27
|
+
cd docs
|
|
28
|
+
poetry run sphinx-build -b html . _build
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "pythonanywhere-core"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.10"
|
|
4
4
|
description = "API wrapper for programmatic management of PythonAnywhere services."
|
|
5
5
|
authors = ["PythonAnywhere <developers@pythonanywhere.com>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -39,3 +39,7 @@ line-length = 120
|
|
|
39
39
|
[build-system]
|
|
40
40
|
requires = ["poetry-core>=1.0.0"]
|
|
41
41
|
build-backend = "poetry.core.masonry.api"
|
|
42
|
+
|
|
43
|
+
[tool.pyright]
|
|
44
|
+
venvPath = "."
|
|
45
|
+
venv = ".venv"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.1.10'
|
|
@@ -15,12 +15,14 @@ PYTHON_VERSIONS: Dict[str, str] = {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
def get_api_endpoint() -> str:
|
|
18
|
+
def get_api_endpoint(username: str, flavor: str) -> str:
|
|
19
19
|
hostname = os.environ.get(
|
|
20
20
|
"PYTHONANYWHERE_SITE",
|
|
21
21
|
"www." + os.environ.get("PYTHONANYWHERE_DOMAIN", "pythonanywhere.com"),
|
|
22
22
|
)
|
|
23
|
-
|
|
23
|
+
if flavor == "websites":
|
|
24
|
+
return f"https://{hostname}/api/v1/user/{username}/{flavor}/"
|
|
25
|
+
return f"https://{hostname}/api/v0/user/{username}/{flavor}/"
|
|
24
26
|
|
|
25
27
|
|
|
26
28
|
def helpful_token_error_message() -> str:
|
|
@@ -41,7 +41,7 @@ class Files:
|
|
|
41
41
|
subdirectories of a directory at `path` (limited to 1000 results)
|
|
42
42
|
"""
|
|
43
43
|
|
|
44
|
-
base_url = get_api_endpoint(
|
|
44
|
+
base_url = get_api_endpoint(username=getpass.getuser(), flavor="files")
|
|
45
45
|
path_endpoint = urljoin(base_url, "path")
|
|
46
46
|
sharing_endpoint = urljoin(base_url, "sharing/")
|
|
47
47
|
tree_endpoint = urljoin(base_url, "tree/")
|
|
@@ -55,6 +55,9 @@ class Files:
|
|
|
55
55
|
return f": {msg}"
|
|
56
56
|
return ""
|
|
57
57
|
|
|
58
|
+
def _make_sharing_url(self, sharing_url_suffix):
|
|
59
|
+
return urljoin(self.base_url.split("api")[0], sharing_url_suffix)
|
|
60
|
+
|
|
58
61
|
def path_get(self, path: str) -> Union[dict, bytes]:
|
|
59
62
|
"""Returns dictionary of directory contents when `path` is an
|
|
60
63
|
absolute path to of an existing directory or file contents if
|
|
@@ -113,19 +116,21 @@ class Files:
|
|
|
113
116
|
f"DELETE on {url} failed, got {result}{self._error_msg(result)}"
|
|
114
117
|
)
|
|
115
118
|
|
|
116
|
-
def sharing_post(self, path: str) -> Tuple[
|
|
119
|
+
def sharing_post(self, path: str) -> Tuple[str, str]:
|
|
117
120
|
"""Starts sharing a file at `path`.
|
|
118
121
|
|
|
119
|
-
Returns a tuple with a
|
|
120
|
-
success, raises otherwise.
|
|
121
|
-
if file has been already shared."""
|
|
122
|
+
Returns a tuple with a message and sharing link on
|
|
123
|
+
success, raises otherwise. Message is "successfully shared" on success,
|
|
124
|
+
"was already shared" if file has been already shared."""
|
|
122
125
|
|
|
123
126
|
url = self.sharing_endpoint
|
|
124
127
|
|
|
125
128
|
result = call_api(url, "POST", json={"path": path})
|
|
126
129
|
|
|
127
130
|
if result.ok:
|
|
128
|
-
|
|
131
|
+
msg = {200: "was already shared", 201: "successfully shared"}[result.status_code]
|
|
132
|
+
sharing_url_suffix = result.json()["url"]
|
|
133
|
+
return msg, self._make_sharing_url(sharing_url_suffix)
|
|
129
134
|
|
|
130
135
|
raise PythonAnywhereApiException(
|
|
131
136
|
f"POST to {url} to share '{path}' failed, got {result}{self._error_msg(result)}"
|
|
@@ -140,8 +145,11 @@ class Files:
|
|
|
140
145
|
url = f"{self.sharing_endpoint}?path={path}"
|
|
141
146
|
|
|
142
147
|
result = call_api(url, "GET")
|
|
143
|
-
|
|
144
|
-
|
|
148
|
+
if result.ok:
|
|
149
|
+
sharing_url_suffix = result.json()["url"]
|
|
150
|
+
return self._make_sharing_url(sharing_url_suffix)
|
|
151
|
+
else:
|
|
152
|
+
return ""
|
|
145
153
|
|
|
146
154
|
def sharing_delete(self, path: str) -> int:
|
|
147
155
|
"""Stops sharing file at `path`.
|
|
@@ -168,4 +176,4 @@ class Files:
|
|
|
168
176
|
if result.ok:
|
|
169
177
|
return result.json()
|
|
170
178
|
|
|
171
|
-
raise PythonAnywhereApiException(f"GET to {url} failed, got {result}{self._error_msg(result)}")
|
|
179
|
+
raise PythonAnywhereApiException(f"GET to {url} failed, got {result}{self._error_msg(result)}")
|
|
@@ -10,7 +10,7 @@ from pythonanywhere_core.exceptions import PythonAnywhereApiException
|
|
|
10
10
|
class Schedule:
|
|
11
11
|
"""Interface for PythonAnywhere scheduled tasks API.
|
|
12
12
|
|
|
13
|
-
Uses `
|
|
13
|
+
Uses `pythonanywhere_core.api` :method: `get_api_endpoint` to create url,
|
|
14
14
|
which is stored in a class variable `Schedule.base_url`, then calls
|
|
15
15
|
`call_api` with appropriate arguments to execute scheduled tasks tasks
|
|
16
16
|
actions. Covers 'GET' and 'POST' methods for tasks list, as well as
|
|
@@ -22,7 +22,7 @@ class Schedule:
|
|
|
22
22
|
Use :method: `Schedule.delete` to delete existing task.
|
|
23
23
|
Use :method: `Schedule.update` to update existing task."""
|
|
24
24
|
|
|
25
|
-
base_url: str = get_api_endpoint(
|
|
25
|
+
base_url: str = get_api_endpoint(username=getpass.getuser(), flavor="schedule")
|
|
26
26
|
|
|
27
27
|
def create(self, params: dict) -> Optional[dict]:
|
|
28
28
|
"""Creates new scheduled task using `params`.
|
|
@@ -21,7 +21,7 @@ class StudentsAPI:
|
|
|
21
21
|
- use :method: `StudentsAPI.delete` to remove a student
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
|
-
base_url: str = get_api_endpoint(
|
|
24
|
+
base_url: str = get_api_endpoint(username=getpass.getuser(), flavor="students")
|
|
25
25
|
|
|
26
26
|
def get(self) -> Optional[dict]:
|
|
27
27
|
"""Returns list of PythonAnywhere students related with user's account."""
|
|
@@ -28,10 +28,9 @@ class Webapp:
|
|
|
28
28
|
|
|
29
29
|
"""
|
|
30
30
|
def __init__(self, domain: str) -> None:
|
|
31
|
-
self.endpoint = get_api_endpoint()
|
|
32
31
|
self.username = getpass.getuser()
|
|
33
|
-
self.files_url =
|
|
34
|
-
self.webapps_url =
|
|
32
|
+
self.files_url = get_api_endpoint(username=self.username, flavor="files")
|
|
33
|
+
self.webapps_url = get_api_endpoint(username=self.username, flavor="webapps")
|
|
35
34
|
self.domain = domain
|
|
36
35
|
self.domain_url = f"{self.webapps_url}{self.domain}/"
|
|
37
36
|
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import getpass
|
|
2
|
+
|
|
3
|
+
from pythonanywhere_core.base import call_api, get_api_endpoint
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Website:
|
|
7
|
+
""" Interface for PythonAnywhere websites API.
|
|
8
|
+
|
|
9
|
+
Uses ``pythonanywhere_core.base`` :method: ``get_api_endpoint`` to
|
|
10
|
+
create url, which is stored in a class variable ``Website.api_endpoint``,
|
|
11
|
+
then calls ``call_api`` with appropriate arguments to execute websites
|
|
12
|
+
action.
|
|
13
|
+
|
|
14
|
+
Use :method: ``Website.create`` to create new website.
|
|
15
|
+
Use :method: ``Website.get`` to get website info.
|
|
16
|
+
Use :method: ``Website.list`` to get all websites list.
|
|
17
|
+
Use :method: ``Website.reload`` to reload website.
|
|
18
|
+
Use :method: ``Website.delete`` to delete website.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
api_endpoint = get_api_endpoint(username=getpass.getuser(), flavor="websites")
|
|
22
|
+
|
|
23
|
+
def create(self, domain_name: str, command: str) -> dict:
|
|
24
|
+
"""Creates new website with ``domain_name`` and ``command``.
|
|
25
|
+
|
|
26
|
+
:param domain_name: domain name for new website
|
|
27
|
+
:param command: command for new website
|
|
28
|
+
:returns: dictionary with created website info"""
|
|
29
|
+
|
|
30
|
+
response = call_api(
|
|
31
|
+
self.api_endpoint,
|
|
32
|
+
"post",
|
|
33
|
+
json={
|
|
34
|
+
"domain_name": domain_name,
|
|
35
|
+
"enabled": True,
|
|
36
|
+
"webapp": {"command": command}
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
return response.json()
|
|
40
|
+
|
|
41
|
+
def get(self, domain_name: str) -> dict:
|
|
42
|
+
"""Returns dictionary with website info for ``domain_name``.
|
|
43
|
+
:param domain_name:
|
|
44
|
+
:return: dictionary with website info"""
|
|
45
|
+
|
|
46
|
+
response = call_api(
|
|
47
|
+
f"{self.api_endpoint}{domain_name}/",
|
|
48
|
+
"get",
|
|
49
|
+
)
|
|
50
|
+
return response.json()
|
|
51
|
+
|
|
52
|
+
def list(self) -> list:
|
|
53
|
+
"""Returns list of dictionaries with all websites info.
|
|
54
|
+
:return: list of dictionaries with websites info"""
|
|
55
|
+
|
|
56
|
+
response = call_api(
|
|
57
|
+
self.api_endpoint,
|
|
58
|
+
"get",
|
|
59
|
+
)
|
|
60
|
+
return response.json()
|
|
61
|
+
|
|
62
|
+
def reload(self, domain_name: str) -> dict:
|
|
63
|
+
"""Reloads website with ``domain_name``.
|
|
64
|
+
:param domain_name: domain name for website to reload
|
|
65
|
+
:return: dictionary with response"""
|
|
66
|
+
|
|
67
|
+
response = call_api(
|
|
68
|
+
f"{self.api_endpoint}{domain_name}/reload/",
|
|
69
|
+
"post",
|
|
70
|
+
)
|
|
71
|
+
return response.json()
|
|
72
|
+
|
|
73
|
+
def delete(self, domain_name: str) -> dict:
|
|
74
|
+
"""Deletes website with ``domain_name``.
|
|
75
|
+
:param domain_name: domain name for website to delete
|
|
76
|
+
:return: empty dictionary"""
|
|
77
|
+
|
|
78
|
+
call_api(
|
|
79
|
+
f"{self.api_endpoint}{domain_name}/",
|
|
80
|
+
"delete",
|
|
81
|
+
)
|
|
82
|
+
return {}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '0.1.8'
|
|
File without changes
|
|
File without changes
|