pythonanywhere-core 0.2.7__py3-none-any.whl → 0.2.9__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/base.py +25 -4
- pythonanywhere_core/exceptions.py +11 -1
- pythonanywhere_core/webapp.py +3 -23
- {pythonanywhere_core-0.2.7.dist-info → pythonanywhere_core-0.2.9.dist-info}/METADATA +17 -5
- pythonanywhere_core-0.2.9.dist-info/RECORD +13 -0
- {pythonanywhere_core-0.2.7.dist-info → pythonanywhere_core-0.2.9.dist-info}/WHEEL +1 -1
- pythonanywhere_core-0.2.7.dist-info/RECORD +0 -13
- {pythonanywhere_core-0.2.7.dist-info → pythonanywhere_core-0.2.9.dist-info/licenses}/LICENSE +0 -0
pythonanywhere_core/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.2.
|
|
1
|
+
__version__ = "0.2.9"
|
pythonanywhere_core/base.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import platform
|
|
2
3
|
from typing import Dict
|
|
3
4
|
|
|
4
5
|
import requests
|
|
5
6
|
|
|
7
|
+
from pythonanywhere_core import __version__
|
|
6
8
|
from pythonanywhere_core.exceptions import AuthenticationError, NoTokenError
|
|
7
9
|
|
|
8
10
|
PYTHON_VERSIONS: Dict[str, str] = {
|
|
@@ -14,6 +16,7 @@ PYTHON_VERSIONS: Dict[str, str] = {
|
|
|
14
16
|
"3.11": "python311",
|
|
15
17
|
"3.12": "python312",
|
|
16
18
|
"3.13": "python313",
|
|
19
|
+
"3.14": "python314",
|
|
17
20
|
}
|
|
18
21
|
|
|
19
22
|
|
|
@@ -53,17 +56,35 @@ def call_api(url: str, method: str, **kwargs) -> requests.Response:
|
|
|
53
56
|
:returns: requests.Response object
|
|
54
57
|
|
|
55
58
|
:raises AuthenticationError: if API returns 401
|
|
56
|
-
:raises NoTokenError: if API_TOKEN environment variable is not set
|
|
59
|
+
:raises NoTokenError: if API_TOKEN environment variable is not set
|
|
60
|
+
|
|
61
|
+
Client identification can be provided via PYTHONANYWHERE_CLIENT environment
|
|
62
|
+
variable (e.g., "pa/1.0.0" or "mcp-server/0.5.0") to help with usage analytics.
|
|
63
|
+
"""
|
|
57
64
|
|
|
58
65
|
token = os.environ.get("API_TOKEN")
|
|
59
66
|
if token is None:
|
|
60
67
|
raise NoTokenError(helpful_token_error_message())
|
|
61
|
-
|
|
68
|
+
|
|
69
|
+
base_user_agent = f"pythonanywhere-core/{__version__}"
|
|
70
|
+
client_info = os.environ.get("PYTHONANYWHERE_CLIENT")
|
|
71
|
+
|
|
72
|
+
if client_info:
|
|
73
|
+
user_agent = f"{base_user_agent} ({client_info}; Python/{platform.python_version()})"
|
|
74
|
+
else:
|
|
75
|
+
user_agent = f"{base_user_agent} (Python/{platform.python_version()})"
|
|
76
|
+
|
|
77
|
+
headers = {
|
|
78
|
+
"Authorization": f"Token {token}",
|
|
79
|
+
"User-Agent": user_agent
|
|
80
|
+
}
|
|
81
|
+
if "headers" in kwargs:
|
|
82
|
+
headers.update(kwargs.pop("headers"))
|
|
83
|
+
|
|
62
84
|
response = requests.request(
|
|
63
85
|
method=method,
|
|
64
86
|
url=url,
|
|
65
|
-
headers=
|
|
66
|
-
verify=not insecure,
|
|
87
|
+
headers=headers,
|
|
67
88
|
**kwargs,
|
|
68
89
|
)
|
|
69
90
|
if response.status_code == 401:
|
|
@@ -15,4 +15,14 @@ class NoTokenError(PythonAnywhereApiException):
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class DomainAlreadyExistsException(PythonAnywhereApiException):
|
|
18
|
-
pass
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class MissingCNAMEException(PythonAnywhereApiException):
|
|
22
|
+
def __init__(self):
|
|
23
|
+
super().__init__(
|
|
24
|
+
"Could not find a CNAME for your website. If you're using an A record, "
|
|
25
|
+
"CloudFlare, or some other way of pointing your domain at PythonAnywhere "
|
|
26
|
+
"then that should not be a problem. If you're not, you should double-check "
|
|
27
|
+
"your DNS setup."
|
|
28
|
+
)
|
pythonanywhere_core/webapp.py
CHANGED
|
@@ -7,10 +7,9 @@ from textwrap import dedent
|
|
|
7
7
|
from typing import Any
|
|
8
8
|
|
|
9
9
|
from dateutil.parser import parse
|
|
10
|
-
from snakesay import snakesay
|
|
11
10
|
|
|
12
11
|
from pythonanywhere_core.base import call_api, get_api_endpoint, PYTHON_VERSIONS
|
|
13
|
-
from pythonanywhere_core.exceptions import SanityException, PythonAnywhereApiException
|
|
12
|
+
from pythonanywhere_core.exceptions import SanityException, PythonAnywhereApiException, MissingCNAMEException
|
|
14
13
|
|
|
15
14
|
|
|
16
15
|
class Webapp:
|
|
@@ -54,7 +53,6 @@ class Webapp:
|
|
|
54
53
|
|
|
55
54
|
:raises SanityException: if API token is missing or webapp already exists
|
|
56
55
|
"""
|
|
57
|
-
print(snakesay("Running API sanity checks"))
|
|
58
56
|
token = os.environ.get("API_TOKEN")
|
|
59
57
|
if not token:
|
|
60
58
|
raise SanityException(
|
|
@@ -127,24 +125,13 @@ class Webapp:
|
|
|
127
125
|
def reload(self) -> None:
|
|
128
126
|
"""Reload webapp
|
|
129
127
|
|
|
128
|
+
:raises MissingCNAMEException: if CNAME not found (reload succeeded)
|
|
130
129
|
:raises PythonAnywhereApiException: if API call fails"""
|
|
131
130
|
url = f"{self.domain_url}reload/"
|
|
132
131
|
response = call_api(url, "post")
|
|
133
132
|
if not response.ok:
|
|
134
133
|
if response.status_code == 409 and response.json()["error"] == "cname_error":
|
|
135
|
-
|
|
136
|
-
snakesay(
|
|
137
|
-
dedent(
|
|
138
|
-
"""
|
|
139
|
-
Could not find a CNAME for your website. If you're using an A record,
|
|
140
|
-
CloudFlare, or some other way of pointing your domain at PythonAnywhere
|
|
141
|
-
then that should not be a problem. If you're not, you should double-check
|
|
142
|
-
your DNS setup.
|
|
143
|
-
"""
|
|
144
|
-
)
|
|
145
|
-
)
|
|
146
|
-
)
|
|
147
|
-
return
|
|
134
|
+
raise MissingCNAMEException()
|
|
148
135
|
raise PythonAnywhereApiException(f"POST to reload webapp via API failed, got {response}:{response.text}")
|
|
149
136
|
|
|
150
137
|
def set_ssl(self, certificate: str, private_key: str) -> None:
|
|
@@ -155,7 +142,6 @@ class Webapp:
|
|
|
155
142
|
|
|
156
143
|
:raises PythonAnywhereApiException: if API call fails
|
|
157
144
|
"""
|
|
158
|
-
print(snakesay(f"Setting up SSL for {self.domain} via API"))
|
|
159
145
|
url = f"{self.domain_url}ssl/"
|
|
160
146
|
response = call_api(url, "post", json={"cert": certificate, "private_key": private_key})
|
|
161
147
|
if not response.ok:
|
|
@@ -193,12 +179,6 @@ class Webapp:
|
|
|
193
179
|
|
|
194
180
|
:raises PythonAnywhereApiException: if API call fails
|
|
195
181
|
"""
|
|
196
|
-
if index:
|
|
197
|
-
message = f"Deleting old (archive number {index}) {log_type} log file for {self.domain} via API"
|
|
198
|
-
else:
|
|
199
|
-
message = f"Deleting current {log_type} log file for {self.domain} via API"
|
|
200
|
-
print(snakesay(message))
|
|
201
|
-
|
|
202
182
|
if index == 1:
|
|
203
183
|
suffix = ".1"
|
|
204
184
|
elif index > 1:
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pythonanywhere-core
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.9
|
|
4
4
|
Summary: API wrapper for programmatic management of PythonAnywhere services.
|
|
5
5
|
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
6
7
|
Keywords: pythonanywhere,api,cloud,web hosting
|
|
7
8
|
Author: PythonAnywhere
|
|
8
9
|
Author-email: developers@pythonanywhere.com
|
|
@@ -15,19 +16,30 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
15
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
16
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
18
20
|
Classifier: Topic :: Software Development :: Libraries
|
|
19
21
|
Requires-Dist: python-dateutil (>=2.8.2,<3.0.0)
|
|
20
22
|
Requires-Dist: requests (>=2.30.0,<3.0.0)
|
|
21
|
-
Requires-Dist: snakesay (>=0.10.3,<0.11.0)
|
|
22
23
|
Requires-Dist: typing_extensions (>=4.5.0,<5.0.0)
|
|
24
|
+
Project-URL: Documentation, https://core.pythonanywhere.com/
|
|
25
|
+
Project-URL: Homepage, https://core.pythonanywhere.com/
|
|
26
|
+
Project-URL: Repository, https://github.com/pythonanywhere/pythonanywhere-core
|
|
23
27
|
Description-Content-Type: text/x-rst
|
|
24
28
|
|
|
25
|
-
|
|
29
|
+
pythonanywhere-core
|
|
30
|
+
===================
|
|
26
31
|
|
|
27
|
-
|
|
32
|
+
Python SDK for PythonAnywhere API - programmatic management of PythonAnywhere
|
|
33
|
+
services including webapps, files, scheduled tasks, students, and websites.
|
|
34
|
+
|
|
35
|
+
Core library behind the `PythonAnywhere cli tool`_.
|
|
28
36
|
|
|
29
37
|
.. _PythonAnywhere cli tool: https://pypi.org/project/pythonanywhere/
|
|
30
38
|
|
|
39
|
+
Documentation
|
|
40
|
+
=============
|
|
41
|
+
|
|
42
|
+
Full documentation is available at https://core.pythonanywhere.com/
|
|
31
43
|
|
|
32
44
|
Development
|
|
33
45
|
===========
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
pythonanywhere_core/__init__.py,sha256=F8OVhAhMXSkvvXYgZtbPn2SG1AQC3joK4yu-FrHt81Y,22
|
|
2
|
+
pythonanywhere_core/base.py,sha256=fMdyaccJdpeB8ZN2QqqEmUPYSaAp3ZdvFxgDexR0Bfg,3195
|
|
3
|
+
pythonanywhere_core/exceptions.py,sha256=37aZy6txVFuiXfUdRJA5d7NQ1N5vbkdOveqv8rwnDbk,691
|
|
4
|
+
pythonanywhere_core/files.py,sha256=qIfbcg472TPeOejpif_i64PtWP_qROlxoz5JbePzX8Q,6491
|
|
5
|
+
pythonanywhere_core/resources.py,sha256=NzzL-eNyo26Lr5FVJSAaxTq0Ia0vP3tIew_N7yFiFlk,1172
|
|
6
|
+
pythonanywhere_core/schedule.py,sha256=giSO-Gssshkto9de2hSA3VeNKotXYu-lBG-iyqDolIw,3904
|
|
7
|
+
pythonanywhere_core/students.py,sha256=PeBLUP7AOCRwCDiaH71wURwFx3H3PLQW91f5AjOPQmU,1778
|
|
8
|
+
pythonanywhere_core/webapp.py,sha256=Fg-c_qnoXYK_VN3bMQjBPoVkRj49KZt_7OkWdqwW_Us,11277
|
|
9
|
+
pythonanywhere_core/website.py,sha256=6Q_37QDKkuKE3771V056aiPmrQ0v8ot6U59CIaZwQzk,4348
|
|
10
|
+
pythonanywhere_core-0.2.9.dist-info/METADATA,sha256=eYEs0j3fTbw5u1ayGidBnCzzFFKzECKpMWWQYPSnWl0,1880
|
|
11
|
+
pythonanywhere_core-0.2.9.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
12
|
+
pythonanywhere_core-0.2.9.dist-info/licenses/LICENSE,sha256=zOJT_E5Fv8crFEKS5ulv0btbjnUiIkdsEBPEBmLHRlQ,1080
|
|
13
|
+
pythonanywhere_core-0.2.9.dist-info/RECORD,,
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
pythonanywhere_core/__init__.py,sha256=XHypfHSPdgXFKmOdoewn7czU670gt8InhHhzlP5j_aA,22
|
|
2
|
-
pythonanywhere_core/base.py,sha256=ro1ByvW1Wlf8Qv2d3NFnHdmM7viujmDJmxUBATqvvHI,2572
|
|
3
|
-
pythonanywhere_core/exceptions.py,sha256=NT6JBtd0VK3PbFc69qx4bnAygA94-pOTghp70xxz1Xw,281
|
|
4
|
-
pythonanywhere_core/files.py,sha256=qIfbcg472TPeOejpif_i64PtWP_qROlxoz5JbePzX8Q,6491
|
|
5
|
-
pythonanywhere_core/resources.py,sha256=NzzL-eNyo26Lr5FVJSAaxTq0Ia0vP3tIew_N7yFiFlk,1172
|
|
6
|
-
pythonanywhere_core/schedule.py,sha256=giSO-Gssshkto9de2hSA3VeNKotXYu-lBG-iyqDolIw,3904
|
|
7
|
-
pythonanywhere_core/students.py,sha256=PeBLUP7AOCRwCDiaH71wURwFx3H3PLQW91f5AjOPQmU,1778
|
|
8
|
-
pythonanywhere_core/webapp.py,sha256=x_66jfGldtQrRhPPQgGFydJnvPreFyCBEYZGqeP0Ql4,12126
|
|
9
|
-
pythonanywhere_core/website.py,sha256=6Q_37QDKkuKE3771V056aiPmrQ0v8ot6U59CIaZwQzk,4348
|
|
10
|
-
pythonanywhere_core-0.2.7.dist-info/LICENSE,sha256=zOJT_E5Fv8crFEKS5ulv0btbjnUiIkdsEBPEBmLHRlQ,1080
|
|
11
|
-
pythonanywhere_core-0.2.7.dist-info/METADATA,sha256=sf8yUXYmAt_0AZAv4jg1Ytr4AUe1r-A2rPlENC4bbP8,1430
|
|
12
|
-
pythonanywhere_core-0.2.7.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
|
13
|
-
pythonanywhere_core-0.2.7.dist-info/RECORD,,
|
{pythonanywhere_core-0.2.7.dist-info → pythonanywhere_core-0.2.9.dist-info/licenses}/LICENSE
RENAMED
|
File without changes
|