pythonanywhere-core 0.3.2__py3-none-any.whl → 0.3.3__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.
- pythonanywhere_core/__init__.py +1 -1
- pythonanywhere_core/files.py +5 -4
- pythonanywhere_core/schedule.py +20 -14
- pythonanywhere_core/webapp.py +19 -4
- pythonanywhere_core/website.py +18 -3
- {pythonanywhere_core-0.3.2.dist-info → pythonanywhere_core-0.3.3.dist-info}/METADATA +1 -1
- pythonanywhere_core-0.3.3.dist-info/RECORD +13 -0
- pythonanywhere_core-0.3.2.dist-info/RECORD +0 -13
- {pythonanywhere_core-0.3.2.dist-info → pythonanywhere_core-0.3.3.dist-info}/WHEEL +0 -0
- {pythonanywhere_core-0.3.2.dist-info → pythonanywhere_core-0.3.3.dist-info}/licenses/LICENSE +0 -0
pythonanywhere_core/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.3.
|
|
1
|
+
__version__ = "0.3.3"
|
pythonanywhere_core/files.py
CHANGED
|
@@ -117,15 +117,16 @@ class Files:
|
|
|
117
117
|
"""Starts sharing a file at `path`.
|
|
118
118
|
|
|
119
119
|
Returns a tuple with a message and sharing link on
|
|
120
|
-
|
|
121
|
-
"was already shared"
|
|
120
|
+
HTTP 200/201, raises otherwise. Message is "successfully shared" on 201,
|
|
121
|
+
"was already shared" on 200."""
|
|
122
122
|
|
|
123
123
|
url = self.sharing_endpoint
|
|
124
124
|
|
|
125
125
|
result = call_api(url, "POST", json={"path": path})
|
|
126
126
|
|
|
127
|
-
|
|
128
|
-
|
|
127
|
+
messages = {200: "was already shared", 201: "successfully shared"}
|
|
128
|
+
if result.status_code in messages:
|
|
129
|
+
msg = messages[result.status_code]
|
|
129
130
|
sharing_url_suffix = result.json()["url"]
|
|
130
131
|
return msg, self._make_sharing_url(sharing_url_suffix)
|
|
131
132
|
|
pythonanywhere_core/schedule.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import List
|
|
1
|
+
from typing import List
|
|
2
2
|
|
|
3
3
|
from typing_extensions import Literal
|
|
4
4
|
|
|
@@ -29,30 +29,31 @@ class Schedule:
|
|
|
29
29
|
|
|
30
30
|
base_url: str = get_api_endpoint(username=get_username(), flavor="schedule")
|
|
31
31
|
|
|
32
|
-
def create(self, params: dict) ->
|
|
32
|
+
def create(self, params: dict) -> dict:
|
|
33
33
|
"""Creates new scheduled task using `params`.
|
|
34
34
|
|
|
35
35
|
Params should be: command, enabled (True or False), interval (daily or
|
|
36
36
|
hourly), hour (24h format) and minute.
|
|
37
37
|
|
|
38
38
|
:param params: dictionary with required scheduled task specs
|
|
39
|
-
:returns: dictionary with created task specs
|
|
39
|
+
:returns: dictionary with created task specs on HTTP 201
|
|
40
|
+
:raises PythonAnywhereApiException: if the response is not HTTP 201"""
|
|
40
41
|
|
|
41
42
|
result = call_api(self.base_url, "POST", json=params)
|
|
42
43
|
|
|
43
44
|
if result.status_code == 201:
|
|
44
45
|
return result.json()
|
|
45
46
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
)
|
|
47
|
+
raise PythonAnywhereApiException(
|
|
48
|
+
f"POST to set new task via API failed, got {result}: {result.text}"
|
|
49
|
+
)
|
|
50
50
|
|
|
51
51
|
def delete(self, task_id: int) -> Literal[True]:
|
|
52
52
|
"""Deletes scheduled task by id.
|
|
53
53
|
|
|
54
54
|
:param task_id: scheduled task to be deleted id number
|
|
55
|
-
:returns: True when API response is 204
|
|
55
|
+
:returns: True when API response is 204
|
|
56
|
+
:raises PythonAnywhereApiException: if the response is not HTTP 204"""
|
|
56
57
|
|
|
57
58
|
result = call_api(
|
|
58
59
|
f"{self.base_url}{task_id}/", "DELETE"
|
|
@@ -61,17 +62,22 @@ class Schedule:
|
|
|
61
62
|
if result.status_code == 204:
|
|
62
63
|
return True
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
)
|
|
65
|
+
raise PythonAnywhereApiException(
|
|
66
|
+
f"DELETE via API on task {task_id} failed, got {result}: {result.text}"
|
|
67
|
+
)
|
|
68
68
|
|
|
69
69
|
def get_list(self) -> List[dict]:
|
|
70
70
|
"""Gets list of existing scheduled tasks.
|
|
71
71
|
|
|
72
|
-
:returns: list of existing scheduled tasks specs
|
|
72
|
+
:returns: list of existing scheduled tasks specs on HTTP 200
|
|
73
|
+
:raises PythonAnywhereApiException: if the response is not HTTP 200"""
|
|
73
74
|
|
|
74
|
-
|
|
75
|
+
result = call_api(self.base_url, "GET")
|
|
76
|
+
if result.status_code != 200:
|
|
77
|
+
raise PythonAnywhereApiException(
|
|
78
|
+
f"GET scheduled tasks via API failed, got {result}: {result.text}"
|
|
79
|
+
)
|
|
80
|
+
return result.json()
|
|
75
81
|
|
|
76
82
|
def get_specs(self, task_id: int) -> dict:
|
|
77
83
|
"""Get task specs by id.
|
pythonanywhere_core/webapp.py
CHANGED
|
@@ -51,6 +51,7 @@ class Webapp:
|
|
|
51
51
|
:param nuke: if True, skip the check for existing webapp
|
|
52
52
|
|
|
53
53
|
:raises SanityException: if API token is missing or webapp already exists
|
|
54
|
+
:raises PythonAnywhereApiException: if the existence check returns neither 200 nor 404
|
|
54
55
|
"""
|
|
55
56
|
token = os.environ.get("API_TOKEN")
|
|
56
57
|
if not token:
|
|
@@ -73,18 +74,28 @@ class Webapp:
|
|
|
73
74
|
f"You already have a webapp for {self.domain}.\n\nUse the --nuke option if you want to replace it."
|
|
74
75
|
)
|
|
75
76
|
|
|
77
|
+
if response.status_code != 404:
|
|
78
|
+
raise PythonAnywhereApiException(
|
|
79
|
+
f"GET to check webapp existence via API failed, got {response}:{response.text}"
|
|
80
|
+
)
|
|
81
|
+
|
|
76
82
|
def create(self, python_version: str, virtualenv_path: Path | None, project_path: Path, nuke: bool) -> None:
|
|
77
83
|
"""Create a webapp for the given domain, using the given python version and virtualenv path
|
|
78
84
|
|
|
79
85
|
:param python_version: python version to use
|
|
80
86
|
:param virtualenv_path: path to the virtualenv, or None to skip setting it
|
|
81
87
|
:param project_path: path to the project
|
|
82
|
-
:param nuke: if True, delete any existing webapp for this domain
|
|
88
|
+
:param nuke: if True, delete any existing webapp for this domain;
|
|
89
|
+
continue only on HTTP 204 (deleted) or 404 (absent)
|
|
83
90
|
|
|
84
91
|
:raises PythonAnywhereApiException: if API call fails
|
|
85
92
|
"""
|
|
86
93
|
if nuke:
|
|
87
|
-
call_api(self.domain_url, "delete")
|
|
94
|
+
response = call_api(self.domain_url, "delete")
|
|
95
|
+
if response.status_code not in (204, 404):
|
|
96
|
+
raise PythonAnywhereApiException(
|
|
97
|
+
f"DELETE before replacing webapp via API failed, got {response}:{response.text}"
|
|
98
|
+
)
|
|
88
99
|
response = call_api(
|
|
89
100
|
self.webapps_url,
|
|
90
101
|
"post",
|
|
@@ -106,10 +117,14 @@ class Webapp:
|
|
|
106
117
|
:param url_path: URL path (e.g., '/static/')
|
|
107
118
|
:param directory_path: Filesystem path to serve (as Path)
|
|
108
119
|
|
|
109
|
-
:raises PythonAnywhereApiException: if
|
|
120
|
+
:raises PythonAnywhereApiException: if the response is not HTTP 201
|
|
110
121
|
"""
|
|
111
122
|
url = f"{self.domain_url}static_files/"
|
|
112
|
-
call_api(url, "post", json=dict(url=url_path, path=str(directory_path)))
|
|
123
|
+
response = call_api(url, "post", json=dict(url=url_path, path=str(directory_path)))
|
|
124
|
+
if response.status_code != 201:
|
|
125
|
+
raise PythonAnywhereApiException(
|
|
126
|
+
f"POST to create static file mapping via API failed, got {response}:{response.text}"
|
|
127
|
+
)
|
|
113
128
|
|
|
114
129
|
def add_default_static_files_mappings(self, project_path: Path) -> None:
|
|
115
130
|
"""Add default static files mappings for /static/ and /media/.
|
pythonanywhere_core/website.py
CHANGED
|
@@ -52,33 +52,48 @@ class Website:
|
|
|
52
52
|
def get(self, domain_name: str) -> dict:
|
|
53
53
|
"""Returns dictionary with website info for ``domain_name``.
|
|
54
54
|
:param domain_name:
|
|
55
|
-
:return: dictionary with website info
|
|
55
|
+
:return: dictionary with website info on HTTP 200
|
|
56
|
+
:raises PythonAnywhereApiException: if the response is not HTTP 200"""
|
|
56
57
|
|
|
57
58
|
response = call_api(
|
|
58
59
|
f"{self.websites_base_url}{domain_name}/",
|
|
59
60
|
"get",
|
|
60
61
|
)
|
|
62
|
+
if response.status_code != 200:
|
|
63
|
+
raise PythonAnywhereApiException(
|
|
64
|
+
f"GET website via API failed, got {response}:{response.text}"
|
|
65
|
+
)
|
|
61
66
|
return response.json()
|
|
62
67
|
|
|
63
68
|
def list(self) -> list:
|
|
64
69
|
"""Returns list of dictionaries with all websites info.
|
|
65
|
-
:return: list of dictionaries with websites info
|
|
70
|
+
:return: list of dictionaries with websites info on HTTP 200
|
|
71
|
+
:raises PythonAnywhereApiException: if the response is not HTTP 200"""
|
|
66
72
|
|
|
67
73
|
response = call_api(
|
|
68
74
|
self.websites_base_url,
|
|
69
75
|
"get",
|
|
70
76
|
)
|
|
77
|
+
if response.status_code != 200:
|
|
78
|
+
raise PythonAnywhereApiException(
|
|
79
|
+
f"GET websites via API failed, got {response}:{response.text}"
|
|
80
|
+
)
|
|
71
81
|
return response.json()
|
|
72
82
|
|
|
73
83
|
def reload(self, domain_name: str) -> dict:
|
|
74
84
|
"""Reloads website with ``domain_name``.
|
|
75
85
|
:param domain_name: domain name for website to reload
|
|
76
|
-
:return: dictionary with response
|
|
86
|
+
:return: dictionary with response on HTTP 200
|
|
87
|
+
:raises PythonAnywhereApiException: if the response is not HTTP 200"""
|
|
77
88
|
|
|
78
89
|
response = call_api(
|
|
79
90
|
f"{self.websites_base_url}{domain_name}/reload/",
|
|
80
91
|
"post",
|
|
81
92
|
)
|
|
93
|
+
if response.status_code != 200:
|
|
94
|
+
raise PythonAnywhereApiException(
|
|
95
|
+
f"POST to reload website via API failed, got {response}:{response.text}"
|
|
96
|
+
)
|
|
82
97
|
return response.json()
|
|
83
98
|
|
|
84
99
|
def auto_ssl(self, domain_name: str) -> dict:
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
pythonanywhere_core/__init__.py,sha256=8KcCYTXH99C2-gCLuPILJvtT9YftRWJsartIx6TQ2ZY,22
|
|
2
|
+
pythonanywhere_core/base.py,sha256=nyyX53D0O5o_1a9rHhJDHUM24wlCr0SS544ZZbXbOYw,3415
|
|
3
|
+
pythonanywhere_core/exceptions.py,sha256=37aZy6txVFuiXfUdRJA5d7NQ1N5vbkdOveqv8rwnDbk,691
|
|
4
|
+
pythonanywhere_core/files.py,sha256=vksw1ZEPvCn9Hv1YrRhc1fea72cuzUzzeyr67u9CvKM,7765
|
|
5
|
+
pythonanywhere_core/resources.py,sha256=VBXspr1sGhfAVSFoTQtwpeN5CDG9-wHZwXJ3-5vy7dY,1167
|
|
6
|
+
pythonanywhere_core/schedule.py,sha256=9YHKvSql9p6VtLGLn7IqXzmdtg2U9ZG287hAcv8Je5g,4261
|
|
7
|
+
pythonanywhere_core/students.py,sha256=VfdftRrwPFEb-m2LW7pbiS9LiffwFNTfHpVeLXMQLLc,1774
|
|
8
|
+
pythonanywhere_core/webapp.py,sha256=dniwSCxogsKuqHI25TyVpk5vTxusrqgJjR4coaq9CYE,12126
|
|
9
|
+
pythonanywhere_core/website.py,sha256=yAosA61uQ7dEIsqX5qUFzNZflWDevMiS25FzNfXeArc,5625
|
|
10
|
+
pythonanywhere_core-0.3.3.dist-info/METADATA,sha256=Wd3dJ_-lZlurXDit_oMnvIr2xUM-nXM-DO_4hWaqNeM,1931
|
|
11
|
+
pythonanywhere_core-0.3.3.dist-info/WHEEL,sha256=L-WvLdSBvJWHXv5zrQxDvNRnIgoBzLUpcbuiw19KCuQ,88
|
|
12
|
+
pythonanywhere_core-0.3.3.dist-info/licenses/LICENSE,sha256=zOJT_E5Fv8crFEKS5ulv0btbjnUiIkdsEBPEBmLHRlQ,1080
|
|
13
|
+
pythonanywhere_core-0.3.3.dist-info/RECORD,,
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
pythonanywhere_core/__init__.py,sha256=vNiWJ14r_cw5t_7UDqDQIVZvladKFGyHH2avsLpN7Vg,22
|
|
2
|
-
pythonanywhere_core/base.py,sha256=nyyX53D0O5o_1a9rHhJDHUM24wlCr0SS544ZZbXbOYw,3415
|
|
3
|
-
pythonanywhere_core/exceptions.py,sha256=37aZy6txVFuiXfUdRJA5d7NQ1N5vbkdOveqv8rwnDbk,691
|
|
4
|
-
pythonanywhere_core/files.py,sha256=UfC7GroBnsIQd_9YYPFhadTDmGc9DC_nEdvFuDWDifM,7741
|
|
5
|
-
pythonanywhere_core/resources.py,sha256=VBXspr1sGhfAVSFoTQtwpeN5CDG9-wHZwXJ3-5vy7dY,1167
|
|
6
|
-
pythonanywhere_core/schedule.py,sha256=payqSYJf92gQzsZwVBtccXH4m_C--AAbdbf5DOtImhs,3900
|
|
7
|
-
pythonanywhere_core/students.py,sha256=VfdftRrwPFEb-m2LW7pbiS9LiffwFNTfHpVeLXMQLLc,1774
|
|
8
|
-
pythonanywhere_core/webapp.py,sha256=inwCDOHY_7IVQXJTeAxucn5vYPpJNdn-JLM3ji5gHwQ,11308
|
|
9
|
-
pythonanywhere_core/website.py,sha256=O4RcvhBDPCdlaF7h432cr-D1XWb7IbeKPh_TuPNnBh4,4815
|
|
10
|
-
pythonanywhere_core-0.3.2.dist-info/METADATA,sha256=9-_iLzlyOEL3EjItF3ciHp59EJ1IUyWF2odjGFH60QM,1931
|
|
11
|
-
pythonanywhere_core-0.3.2.dist-info/WHEEL,sha256=L-WvLdSBvJWHXv5zrQxDvNRnIgoBzLUpcbuiw19KCuQ,88
|
|
12
|
-
pythonanywhere_core-0.3.2.dist-info/licenses/LICENSE,sha256=zOJT_E5Fv8crFEKS5ulv0btbjnUiIkdsEBPEBmLHRlQ,1080
|
|
13
|
-
pythonanywhere_core-0.3.2.dist-info/RECORD,,
|
|
File without changes
|
{pythonanywhere_core-0.3.2.dist-info → pythonanywhere_core-0.3.3.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|