pythonanywhere-core 0.1.10__tar.gz → 0.2.0__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.10 → pythonanywhere_core-0.2.0}/PKG-INFO +2 -1
- {pythonanywhere_core-0.1.10 → pythonanywhere_core-0.2.0}/pyproject.toml +1 -1
- pythonanywhere_core-0.2.0/pythonanywhere_core/__init__.py +1 -0
- {pythonanywhere_core-0.1.10 → pythonanywhere_core-0.2.0}/pythonanywhere_core/base.py +1 -1
- {pythonanywhere_core-0.1.10 → pythonanywhere_core-0.2.0}/pythonanywhere_core/files.py +27 -30
- {pythonanywhere_core-0.1.10 → pythonanywhere_core-0.2.0}/pythonanywhere_core/schedule.py +19 -13
- {pythonanywhere_core-0.1.10 → pythonanywhere_core-0.2.0}/pythonanywhere_core/students.py +20 -12
- {pythonanywhere_core-0.1.10 → pythonanywhere_core-0.2.0}/pythonanywhere_core/webapp.py +7 -7
- pythonanywhere_core-0.2.0/pythonanywhere_core/website.py +111 -0
- pythonanywhere_core-0.1.10/pythonanywhere_core/__init__.py +0 -1
- pythonanywhere_core-0.1.10/pythonanywhere_core/website.py +0 -82
- {pythonanywhere_core-0.1.10 → pythonanywhere_core-0.2.0}/LICENSE +0 -0
- {pythonanywhere_core-0.1.10 → pythonanywhere_core-0.2.0}/README.rst +0 -0
- {pythonanywhere_core-0.1.10 → pythonanywhere_core-0.2.0}/pythonanywhere_core/exceptions.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pythonanywhere-core
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: API wrapper for programmatic management of PythonAnywhere services.
|
|
5
5
|
License: MIT
|
|
6
6
|
Keywords: pythonanywhere,api,cloud,web hosting
|
|
@@ -16,6 +16,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.10
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
20
|
Classifier: Topic :: Software Development :: Libraries
|
|
20
21
|
Requires-Dist: python-dateutil (>=2.8.2,<3.0.0)
|
|
21
22
|
Requires-Dist: requests (>=2.30.0,<3.0.0)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '0.2.0'
|
|
@@ -20,7 +20,7 @@ def get_api_endpoint(username: str, flavor: str) -> str:
|
|
|
20
20
|
"PYTHONANYWHERE_SITE",
|
|
21
21
|
"www." + os.environ.get("PYTHONANYWHERE_DOMAIN", "pythonanywhere.com"),
|
|
22
22
|
)
|
|
23
|
-
if flavor == "websites":
|
|
23
|
+
if flavor == "websites" or flavor == "domains":
|
|
24
24
|
return f"https://{hostname}/api/v1/user/{username}/{flavor}/"
|
|
25
25
|
return f"https://{hostname}/api/v0/user/{username}/{flavor}/"
|
|
26
26
|
|
|
@@ -9,37 +9,34 @@ from pythonanywhere_core.exceptions import PythonAnywhereApiException
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class Files:
|
|
12
|
-
""" Interface for PythonAnywhere files API.
|
|
13
|
-
|
|
14
|
-
Uses `pythonanywhere_core.base` :method: `get_api_endpoint` to
|
|
15
|
-
create url, which is stored in a class variable `Files.base_url`,
|
|
16
|
-
then calls `call_api` with appropriate arguments to execute files
|
|
17
|
-
action.
|
|
18
|
-
|
|
19
|
-
Covers:
|
|
20
|
-
- GET, POST and DELETE for files path endpoint
|
|
21
|
-
- POST, GET and DELETE for files sharing endpoint
|
|
22
|
-
- GET for tree endpoint
|
|
23
|
-
|
|
24
|
-
"path" methods:
|
|
25
|
-
- use :method: `Files.path_get` to get contents of file or
|
|
26
|
-
directory from `path`
|
|
27
|
-
- use :method: `Files.path_post` to upload or update file at given
|
|
28
|
-
`dest_path` using contents from `source`
|
|
29
|
-
- use :method: `Files.path_delete` to delete file/directory on on
|
|
30
|
-
given `path`
|
|
31
|
-
|
|
32
|
-
"sharing" methods:
|
|
33
|
-
- use :method: `Files.sharing_post` to enable sharing a file from
|
|
34
|
-
`path` (if not shared before) and get a link to it
|
|
35
|
-
- use :method: `Files.sharing_get` to get sharing url for `path`
|
|
36
|
-
- use :method: `Files.sharing_delete` to disable sharing for
|
|
37
|
-
`path`
|
|
38
|
-
|
|
39
|
-
"tree" method:
|
|
40
|
-
- use :method: `Files.tree_get` to get list of regular files and
|
|
41
|
-
subdirectories of a directory at `path` (limited to 1000 results)
|
|
42
12
|
"""
|
|
13
|
+
Interface for the PythonAnywhere Files API.
|
|
14
|
+
|
|
15
|
+
This class uses the `get_api_endpoint` function from ``pythonanywhere_core.base``
|
|
16
|
+
to construct the API URL, which is stored in the class variable ``base_url``.
|
|
17
|
+
It then calls the ``call_api`` method with the appropriate arguments to
|
|
18
|
+
perform file-related actions.
|
|
19
|
+
|
|
20
|
+
Supported Endpoints:
|
|
21
|
+
- `GET`, `POST`, and `DELETE` for the files path endpoint.
|
|
22
|
+
- `POST`, `GET`, and `DELETE` for the file sharing endpoint.
|
|
23
|
+
- `GET` for the tree endpoint.
|
|
24
|
+
|
|
25
|
+
Path Methods:
|
|
26
|
+
- :meth:`Files.path_get`: Retrieve the contents of a file or directory from a specified `path`.
|
|
27
|
+
- :meth:`Files.path_post`: Upload or update a file at the given `dest_path` using contents from `source`.
|
|
28
|
+
- :meth:`Files.path_delete`: Delete a file or directory at the specified `path`.
|
|
29
|
+
|
|
30
|
+
Sharing Methods:
|
|
31
|
+
- :meth:`Files.sharing_post`: Enable sharing of a file from the given `path` (if not already shared) and get a link to it.
|
|
32
|
+
- :meth:`Files.sharing_get`: Retrieve the sharing URL for a specified `path`.
|
|
33
|
+
- :meth:`Files.sharing_delete`: Disable sharing for a specified `path`.
|
|
34
|
+
|
|
35
|
+
Tree Method:
|
|
36
|
+
- :meth:`Files.tree_get`: Retrieve a list of regular files and subdirectories of a directory at the specified `path`
|
|
37
|
+
(limited to 1000 results).
|
|
38
|
+
"""
|
|
39
|
+
|
|
43
40
|
|
|
44
41
|
base_url = get_api_endpoint(username=getpass.getuser(), flavor="files")
|
|
45
42
|
path_endpoint = urljoin(base_url, "path")
|
|
@@ -8,19 +8,25 @@ from pythonanywhere_core.exceptions import PythonAnywhereApiException
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class Schedule:
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
11
|
+
"""
|
|
12
|
+
Interface for the PythonAnywhere Scheduled Tasks API.
|
|
13
|
+
|
|
14
|
+
This class uses the `get_api_endpoint` function from ``pythonanywhere_core.api``
|
|
15
|
+
to construct the API URL, which is stored in the class variable ``base_url``.
|
|
16
|
+
It then calls the ``call_api`` method with appropriate arguments to perform
|
|
17
|
+
actions related to scheduled tasks.
|
|
18
|
+
|
|
19
|
+
Supported HTTP Methods:
|
|
20
|
+
- `GET` and `POST` for the tasks list.
|
|
21
|
+
- `GET`, `PATCH`, and `DELETE` for tasks with an ID.
|
|
22
|
+
|
|
23
|
+
Methods:
|
|
24
|
+
- :meth:`Schedule.get_list`: Retrieve the list of all scheduled tasks.
|
|
25
|
+
- :meth:`Schedule.create`: Create a new scheduled task.
|
|
26
|
+
- :meth:`Schedule.get_specs`: Retrieve the specifications of an existing task.
|
|
27
|
+
- :meth:`Schedule.delete`: Delete an existing task.
|
|
28
|
+
- :meth:`Schedule.update`: Update an existing task.
|
|
29
|
+
"""
|
|
24
30
|
|
|
25
31
|
base_url: str = get_api_endpoint(username=getpass.getuser(), flavor="schedule")
|
|
26
32
|
|
|
@@ -5,26 +5,30 @@ from pythonanywhere_core.base import call_api, get_api_endpoint
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class StudentsAPI:
|
|
8
|
-
"""
|
|
8
|
+
"""
|
|
9
|
+
Interface for the PythonAnywhere Students API.
|
|
9
10
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
This class uses the `get_api_endpoint` function from
|
|
12
|
+
``pythonanywhere.api.base`` to construct the API URL, which is stored
|
|
13
|
+
in the class variable ``base_url``. It then calls the ``call_api`` method
|
|
14
|
+
with the appropriate arguments to perform student-related actions.
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
Supported HTTP Methods:
|
|
17
|
+
- `GET`
|
|
18
|
+
- `DELETE`
|
|
18
19
|
|
|
19
20
|
Methods:
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
- :meth:`StudentsAPI.get`: Retrieve a list of students.
|
|
22
|
+
- :meth:`StudentsAPI.delete`: Remove a student.
|
|
22
23
|
"""
|
|
23
24
|
|
|
24
25
|
base_url: str = get_api_endpoint(username=getpass.getuser(), flavor="students")
|
|
25
26
|
|
|
26
27
|
def get(self) -> Optional[dict]:
|
|
27
|
-
"""Returns list of PythonAnywhere students related with user's account.
|
|
28
|
+
"""Returns list of PythonAnywhere students related with user's account.
|
|
29
|
+
|
|
30
|
+
:returns: dictionary with students info
|
|
31
|
+
"""
|
|
28
32
|
|
|
29
33
|
result = call_api(self.base_url, "GET")
|
|
30
34
|
|
|
@@ -34,7 +38,11 @@ class StudentsAPI:
|
|
|
34
38
|
raise Exception(f"GET to list students failed, got {result.text}")
|
|
35
39
|
|
|
36
40
|
def delete(self, student_username: str) -> Optional[int]:
|
|
37
|
-
"""Returns 204 if student has been successfully removed, raises otherwise.
|
|
41
|
+
"""Returns 204 if student has been successfully removed, raises otherwise.
|
|
42
|
+
|
|
43
|
+
:param student_username: student username to be removed
|
|
44
|
+
:returns: 204 if student has been successfully removed
|
|
45
|
+
"""
|
|
38
46
|
|
|
39
47
|
url = f"{self.base_url}{student_username}"
|
|
40
48
|
|
|
@@ -19,13 +19,13 @@ class Webapp:
|
|
|
19
19
|
then calls `call_api` with appropriate arguments to execute webapps
|
|
20
20
|
action.
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
Methods:
|
|
23
|
+
- :meth:`Webapp.create`: Create a new webapp.
|
|
24
|
+
- :meth:`Webapp.reload`: Reload the webapp.
|
|
25
|
+
- :meth:`Webapp.set_ssl`: Set the SSL certificate and private key.
|
|
26
|
+
- :meth:`Webapp.get_ssl_info`: Retrieve SSL certificate information.
|
|
27
|
+
- :meth:`Webapp.delete_log`: Delete a log file.
|
|
28
|
+
- :meth:`Webapp.get_log_info`: Retrieve log file information.
|
|
29
29
|
"""
|
|
30
30
|
def __init__(self, domain: str) -> None:
|
|
31
31
|
self.username = getpass.getuser()
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import getpass
|
|
2
|
+
|
|
3
|
+
from pythonanywhere_core.base import call_api, get_api_endpoint
|
|
4
|
+
from pythonanywhere_core.exceptions import PythonAnywhereApiException
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Website:
|
|
9
|
+
""" Interface for PythonAnywhere websites API.
|
|
10
|
+
|
|
11
|
+
Uses ``pythonanywhere_core.base`` function ``get_api_endpoint`` to
|
|
12
|
+
create url, which is stored in a class variable ``Website.api_endpoint``,
|
|
13
|
+
then calls ``call_api`` with appropriate arguments to execute websites
|
|
14
|
+
action.
|
|
15
|
+
|
|
16
|
+
Methods:
|
|
17
|
+
- :meth:`Website.create`: Create a new website.
|
|
18
|
+
- :meth:`Website.get`: Retrieve information about a specific website.
|
|
19
|
+
- :meth:`Website.list`: Get a list of all websites.
|
|
20
|
+
- :meth:`Website.reload`: Reload the website.
|
|
21
|
+
- :meth:`Website.auto_ssl`: Create and apply a Let's Encrypt SSL certificate.
|
|
22
|
+
- :meth:`Website.get_ssl_info`: Get SSL certificate information.
|
|
23
|
+
- :meth:`Website.delete`: Delete a website.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self) -> None:
|
|
27
|
+
self.websites_base_url = get_api_endpoint(username=getpass.getuser(), flavor="websites")
|
|
28
|
+
self.domains_base_url = get_api_endpoint(username=getpass.getuser(), flavor="domains")
|
|
29
|
+
|
|
30
|
+
def create(self, domain_name: str, command: str) -> dict:
|
|
31
|
+
"""Creates new website with ``domain_name`` and ``command``.
|
|
32
|
+
|
|
33
|
+
:param domain_name: domain name for new website
|
|
34
|
+
:param command: command for new website
|
|
35
|
+
:returns: dictionary with created website info"""
|
|
36
|
+
|
|
37
|
+
response = call_api(
|
|
38
|
+
self.websites_base_url,
|
|
39
|
+
"post",
|
|
40
|
+
json={
|
|
41
|
+
"domain_name": domain_name,
|
|
42
|
+
"enabled": True,
|
|
43
|
+
"webapp": {"command": command}
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
return response.json()
|
|
47
|
+
|
|
48
|
+
def get(self, domain_name: str) -> dict:
|
|
49
|
+
"""Returns dictionary with website info for ``domain_name``.
|
|
50
|
+
:param domain_name:
|
|
51
|
+
:return: dictionary with website info"""
|
|
52
|
+
|
|
53
|
+
response = call_api(
|
|
54
|
+
f"{self.websites_base_url}{domain_name}/",
|
|
55
|
+
"get",
|
|
56
|
+
)
|
|
57
|
+
return response.json()
|
|
58
|
+
|
|
59
|
+
def list(self) -> list:
|
|
60
|
+
"""Returns list of dictionaries with all websites info.
|
|
61
|
+
:return: list of dictionaries with websites info"""
|
|
62
|
+
|
|
63
|
+
response = call_api(
|
|
64
|
+
self.websites_base_url,
|
|
65
|
+
"get",
|
|
66
|
+
)
|
|
67
|
+
return response.json()
|
|
68
|
+
|
|
69
|
+
def reload(self, domain_name: str) -> dict:
|
|
70
|
+
"""Reloads website with ``domain_name``.
|
|
71
|
+
:param domain_name: domain name for website to reload
|
|
72
|
+
:return: dictionary with response"""
|
|
73
|
+
|
|
74
|
+
response = call_api(
|
|
75
|
+
f"{self.websites_base_url}{domain_name}/reload/",
|
|
76
|
+
"post",
|
|
77
|
+
)
|
|
78
|
+
return response.json()
|
|
79
|
+
|
|
80
|
+
def auto_ssl(self, domain_name: str) -> dict:
|
|
81
|
+
"""Creates and applies a Let's Encrypt certificate for ``domain_name``.
|
|
82
|
+
:param domain_name: domain name for website to apply the certificate to
|
|
83
|
+
:return: dictionary with response"""
|
|
84
|
+
response = call_api(
|
|
85
|
+
f"{self.domains_base_url}{domain_name}/ssl/",
|
|
86
|
+
"post",
|
|
87
|
+
json={"cert_type": "letsencrypt-auto-renew"}
|
|
88
|
+
)
|
|
89
|
+
return response.json()
|
|
90
|
+
|
|
91
|
+
def get_ssl_info(self, domain_name) -> dict:
|
|
92
|
+
"""Get SSL certificate info
|
|
93
|
+
:param domain_name: domain name for website to get SSL info
|
|
94
|
+
:return: dictionary with SSL certificate info"""
|
|
95
|
+
url = f"{self.domains_base_url}{domain_name}/ssl/"
|
|
96
|
+
response = call_api(url, "get")
|
|
97
|
+
if not response.ok:
|
|
98
|
+
raise PythonAnywhereApiException(f"GET SSL details via API failed, got {response}:{response.text}")
|
|
99
|
+
|
|
100
|
+
return response.json()
|
|
101
|
+
|
|
102
|
+
def delete(self, domain_name: str) -> dict:
|
|
103
|
+
"""Deletes website with ``domain_name``.
|
|
104
|
+
:param domain_name: domain name for website to delete
|
|
105
|
+
:return: empty dictionary"""
|
|
106
|
+
|
|
107
|
+
call_api(
|
|
108
|
+
f"{self.websites_base_url}{domain_name}/",
|
|
109
|
+
"delete",
|
|
110
|
+
)
|
|
111
|
+
return {}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = '0.1.10'
|
|
@@ -1,82 +0,0 @@
|
|
|
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 {}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|