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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pythonanywhere-core
3
- Version: 0.1.8
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 an extraction and improvement of core code behind `PythonAnywhere cli tool`_.
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.8"
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
- return f"https://{hostname}/api/v0/user/{{username}}/{{flavor}}/"
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().format(username=getpass.getuser(), flavor="files")
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[int, str]:
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 status code and sharing link on
120
- success, raises otherwise. Status code is 201 on success, 200
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
- return result.status_code, result.json()["url"]
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
- return result.json()["url"] if result.ok else ""
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 `pythonanywhere.api` :method: `get_api_endpoint` to create url,
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().format(username=getpass.getuser(), flavor="schedule")
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().format(username=getpass.getuser(), flavor="students")
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 = 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,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,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.8'