pythonanywhere-core 0.1.9__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pythonanywhere-core
3
- Version: 0.1.9
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)
@@ -25,7 +26,30 @@ Description-Content-Type: text/x-rst
25
26
 
26
27
  API wrapper for programmatic management of PythonAnywhere services.
27
28
 
28
- It's an extraction and improvement of core code behind `PythonAnywhere cli tool`_.
29
+ It's a core code behind `PythonAnywhere cli tool`_.
29
30
 
30
31
  .. _PythonAnywhere cli tool: https://pypi.org/project/pythonanywhere/
31
32
 
33
+
34
+ Development
35
+ ===========
36
+
37
+ To create local development environment, run:
38
+
39
+ .. code-block:: shell
40
+
41
+ poetry install
42
+
43
+ To run tests:
44
+
45
+ .. code-block:: shell
46
+
47
+ poetry run pytest
48
+
49
+ To build docs:
50
+
51
+ .. code-block:: shell
52
+
53
+ cd docs
54
+ poetry run sphinx-build -b html . _build
55
+
@@ -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.9"
3
+ version = "0.2.0"
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.2.0'
@@ -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
- return f"https://{hostname}/api/v0/user/{{username}}/{{flavor}}/"
23
+ if flavor == "websites" or flavor == "domains":
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:
@@ -9,39 +9,36 @@ 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
- base_url = get_api_endpoint().format(username=getpass.getuser(), flavor="files")
41
+ base_url = get_api_endpoint(username=getpass.getuser(), flavor="files")
45
42
  path_endpoint = urljoin(base_url, "path")
46
43
  sharing_endpoint = urljoin(base_url, "sharing/")
47
44
  tree_endpoint = urljoin(base_url, "tree/")
@@ -116,7 +113,7 @@ class Files:
116
113
  f"DELETE on {url} failed, got {result}{self._error_msg(result)}"
117
114
  )
118
115
 
119
- def sharing_post(self, path: str) -> Tuple[int, str]:
116
+ def sharing_post(self, path: str) -> Tuple[str, str]:
120
117
  """Starts sharing a file at `path`.
121
118
 
122
119
  Returns a tuple with a message and sharing link on
@@ -176,4 +173,4 @@ class Files:
176
173
  if result.ok:
177
174
  return result.json()
178
175
 
179
- raise PythonAnywhereApiException(f"GET to {url} failed, got {result}{self._error_msg(result)}")
176
+ raise PythonAnywhereApiException(f"GET to {url} failed, got {result}{self._error_msg(result)}")
@@ -8,21 +8,27 @@ from pythonanywhere_core.exceptions import PythonAnywhereApiException
8
8
 
9
9
 
10
10
  class Schedule:
11
- """Interface for PythonAnywhere scheduled tasks API.
12
-
13
- Uses `pythonanywhere.api` :method: `get_api_endpoint` to create url,
14
- which is stored in a class variable `Schedule.base_url`, then calls
15
- `call_api` with appropriate arguments to execute scheduled tasks tasks
16
- actions. Covers 'GET' and 'POST' methods for tasks list, as well as
17
- 'GET', 'PATCH' and 'DELETE' methods for task with id.
18
-
19
- Use :method: `Schedule.get_list` to get all tasks list.
20
- Use :method: `Schedule.create` to create new task.
21
- Use :method: `Schedule.get_specs` to get existing task specs.
22
- Use :method: `Schedule.delete` to delete existing task.
23
- Use :method: `Schedule.update` to update existing task."""
24
-
25
- base_url: str = get_api_endpoint().format(username=getpass.getuser(), flavor="schedule")
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
+ """
30
+
31
+ base_url: str = get_api_endpoint(username=getpass.getuser(), flavor="schedule")
26
32
 
27
33
  def create(self, params: dict) -> Optional[dict]:
28
34
  """Creates new scheduled task using `params`.
@@ -5,26 +5,30 @@ from pythonanywhere_core.base import call_api, get_api_endpoint
5
5
 
6
6
 
7
7
  class StudentsAPI:
8
- """Interface for PythonAnywhere students API.
8
+ """
9
+ Interface for the PythonAnywhere Students API.
9
10
 
10
- Uses `pythonanywhere.api.base` :method: `get_api_endpoint` to
11
- create url, which is stored in a class variable `StudentsAPI.base_url`,
12
- then calls `call_api` with appropriate arguments to execute student
13
- action.
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
- Covers:
16
- - GET
17
- - DELETE
16
+ Supported HTTP Methods:
17
+ - `GET`
18
+ - `DELETE`
18
19
 
19
20
  Methods:
20
- - use :method: `StudentsAPI.get` to get list of students
21
- - use :method: `StudentsAPI.delete` to remove a student
21
+ - :meth:`StudentsAPI.get`: Retrieve a list of students.
22
+ - :meth:`StudentsAPI.delete`: Remove a student.
22
23
  """
23
24
 
24
- base_url: str = get_api_endpoint().format(username=getpass.getuser(), flavor="students")
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,19 +19,18 @@ class Webapp:
19
19
  then calls `call_api` with appropriate arguments to execute webapps
20
20
  action.
21
21
 
22
- Use :method: `Webapp.create` to create new webapp.
23
- Use :method: `Webapp.reload` to reload webapp.
24
- Use :method: `Webapp.set_ssl` to set SSL certificate and private key.
25
- Use :method: `Webapp.get_ssl_info` to get SSL certificate info.
26
- Use :method: `Webapp.delete_log` to delete log file.
27
- Use :method: `Webapp.get_log_info` to get log files info.
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
- self.endpoint = get_api_endpoint()
32
31
  self.username = getpass.getuser()
33
- self.files_url = self.endpoint.format(username=self.username, flavor="files")
34
- self.webapps_url = self.endpoint.format(username=self.username, flavor="webapps")
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,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,5 +0,0 @@
1
- API wrapper for programmatic management of PythonAnywhere services.
2
-
3
- It's an extraction and improvement of core code behind `PythonAnywhere cli tool`_.
4
-
5
- .. _PythonAnywhere cli tool: https://pypi.org/project/pythonanywhere/
@@ -1 +0,0 @@
1
- __version__ = '0.1.9'