Skip to content

Commit

Permalink
Merge branch 'master' into t29-files-scripts
Browse files Browse the repository at this point in the history
  • Loading branch information
caseneuve committed Jan 29, 2022
2 parents 06d6d4f + c2e8444 commit 96d5377
Show file tree
Hide file tree
Showing 8 changed files with 130 additions and 41 deletions.
40 changes: 40 additions & 0 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Tests

on: [push]

jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [ '3.6', '3.7', '3.8', '3.9', '3.10' ]

name: Python ${{ matrix.python-version }}
steps:

- uses: actions/checkout@v2

- name: Setup timezone
uses: zcong1993/setup-timezone@master
with:
timezone: UTC

- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}


- name: Install Python dependencies
run: |
python3 -m pip install --upgrade pip
pip3 install -r requirements.txt
pip3 install -e .
- name: Test with pytest
run: |
pytest
- name: Check coverage
run: |
pytest --cov=cli --cov=pythonanywhere --cov=scripts --cov-fail-under=65
64 changes: 40 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,60 @@
[![Build Status](https://travis-ci.org/pythonanywhere/helper_scripts.svg?branch=master)](https://travis-ci.org/pythonanywhere/helper_scripts)
![Build Status](https://github.com/pythonanywhere/helper_scripts/actions/workflows/tests.yaml/badge.svg)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![PyPI](https://img.shields.io/pypi/v/pythonanywhere)](https://pypi.org/project/pythonanywhere/)
[![Downloads](https://pepy.tech/badge/pythonanywhere)](https://pepy.tech/project/pythonanywhere)

# PythonAnywhere helper scripts
# PythonAnywhere cli tool

These scripts are designed to be run from PythonAnywhere consoles
`pa` is a single command to manage PythonAnywhere services.

It is designed to be run from PythonAnywhere consoles, but many subcommands can be executed directly
from your own machine (see [usage](#Usage) below).

## Installing
### On PythonAnywhere
In a PythonAnywhere Bash console, run:

pip3.9 install --user pythonanywhere

pip3.6 install --user pythonanywhere
If there is no `python3.9` on your PythonAnywhere account,
you should upgrade your account to the newest system image.
See [here](https://help.pythonanywhere.com/pages/ChangingSystemImage) how to do that.
`pa` works with python 3.6, 3.7 and 3.8, but we recommend using the latest system image.

### On your own machine
Install the `pythonanywhere` package from [PyPI](https://pypi.org/project/pythonanywhere/).
We recommend using `pipx` if you want to use it only as a cli tool, or a virtual environment
if you want to use a programmatic interface in your own code.

If there is no `python3.6` on your PythonAnywhere account,
you should contact [[email protected]](mailto:[email protected]) and ask for an upgrade.

## Usage

There are two ways to use that package. You can just run the scripts or use underlying api wrappers directly in your scripts.
There are two ways to use the package. You can just run the scripts or use the underlying api wrappers directly in your scripts.

### Command line interface

### Running `pa` on your local machine

`pa` expects the presence of some environment variables that are provided when you run your code in a PythonAnywere console.
You need to provide them if you run `pa` on your local machine.

`API_TOKEN` -- you need to set this to allow `pa` to connect to the [PythonAnywere API](https://help.pythonanywhere.com/pages/API).
To get an API token, log into PythonAnywhere and go to the "Account" page using the link at the top right.
Click on the "API token" tab, and click the "Create a new API token" button to get your token.

There are scripts provided for dealing with web apps:
`PYTHONANYWHERE_SITE` is used to connect to PythonAnywhere API and defaults to `www.pythonanywhere.com`,
but you may need to set it to `eu.pythonanywhere.com` if you use our EU site.

* [pa_autoconfigure_django.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_autoconfigure_django.py)
* [pa_create_webapp_with_virtualenv.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_create_webapp_with_virtualenv.py)
* [pa_delete_webapp_logs.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_delete_webapp_logs.py)
* [pa_install_webapp_letsencrypt_ssl.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_install_webapp_letsencrypt_ssl.py)
* [pa_install_webapp_ssl.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_install_webapp_ssl.py)
* [pa_reload_webapp.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_reload_webapp.py)
* [pa_start_django_webapp_with_virtualenv.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_start_django_webapp_with_virtualenv.py)
If your username on PythonAnywhere is different from the username on your local machine,
you may need to set `USER` for the environment you run `pa` in.

and scheduled tasks:
### Programmatic usage in your code

* [pa_create_scheduled_task.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_create_scheduled_task.py)
* [pa_delete_scheduled_task.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_delete_scheduled_task.py)
* [pa_get_scheduled_tasks_list.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_get_scheduled_tasks_list.py)
* [pa_get_scheduled_task_specs.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_get_scheduled_task_specs.py)
* [pa_update_scheduled_task.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_update_scheduled_task.py)
Take a look at the [`pythonanywhere.task`](https://github.com/pythonanywhere/helper_scripts/blob/master/pythonanywhere/task.py)
module and docstrings of `pythonanywhere.task.Task` class and its methods.

Run any of them with `--help` flag to get information about usage.
### Legacy scripts

See the [blog post](https://blog.pythonanywhere.com/155/)
Some legacy [scripts](https://github.com/pythonanywhere/helper_scripts/blob/master/legacy.md) (separate for each action) are still available.

## Contributing

Expand Down
27 changes: 27 additions & 0 deletions legacy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Legacy scripts

We still provide separate scripts for specific actions that are now all integrated
into unified `pa` cli tool. We will keep them available for people who rely on them in
their workflow, but we plan to drop them when we release 1.0.

There are scripts provided for dealing with web apps:

* [pa_autoconfigure_django.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_autoconfigure_django.py)
* [pa_create_webapp_with_virtualenv.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_create_webapp_with_virtualenv.py)
* [pa_delete_webapp_logs.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_delete_webapp_logs.py)
* [pa_install_webapp_letsencrypt_ssl.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_install_webapp_letsencrypt_ssl.py)
* [pa_install_webapp_ssl.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_install_webapp_ssl.py)
* [pa_reload_webapp.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_reload_webapp.py)
* [pa_start_django_webapp_with_virtualenv.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_start_django_webapp_with_virtualenv.py)

and scheduled tasks:

* [pa_create_scheduled_task.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_create_scheduled_task.py)
* [pa_delete_scheduled_task.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_delete_scheduled_task.py)
* [pa_get_scheduled_tasks_list.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_get_scheduled_tasks_list.py)
* [pa_get_scheduled_task_specs.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_get_scheduled_task_specs.py)
* [pa_update_scheduled_task.py](https://github.com/pythonanywhere/helper_scripts/blob/master/scripts/pa_update_scheduled_task.py)

Run any of them with `--help` flag to get information about usage.

See the [blog post](https://blog.pythonanywhere.com/155/) about how it all started.
8 changes: 5 additions & 3 deletions pythonanywhere/api/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@
import requests

PYTHON_VERSIONS = {
"3.6": "python36", "3.7": "python37", "3.8": "python38", "3.9": "python39",
"3.6": "python36",
"3.7": "python37",
"3.8": "python38",
"3.9": "python39",
"3.10": "python310",
}


Expand Down Expand Up @@ -49,5 +53,3 @@ def call_api(url, method, **kwargs):
f"Authentication error {response.status_code} calling API: {response.text}"
)
return response


16 changes: 9 additions & 7 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
python-dateutil==2.8.1
click==8.0.3
docopt==0.6.2
packaging
psutil==5.7.0
pytest==5.4.2
pytest-cov==2.8.1
pytest-mock==3.1.0
pytest==6.2.5
pytest-cov==3.0.0
pytest-mock==3.6.1
pytest-mypy==0.6.2
requests==2.23.0
responses==0.10.14
requests==2.26.0
responses==0.16.0
schema==0.7.2
tabulate==0.8.7
typer==0.3.2
tabulate==0.8.9
typer==0.4.0
urllib3==1.26.7
virtualenvwrapper==4.8.4
4 changes: 3 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

setup(
name="pythonanywhere",
version="0.9.8",
version="0.9.11",
description="PythonAnywhere helper tools for users",
long_description=long_description,
long_description_content_type="text/markdown",
Expand All @@ -22,6 +22,8 @@
"Intended Audience :: Developers",
"Topic :: Software Development :: Libraries",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.6",
Expand Down
8 changes: 4 additions & 4 deletions tests/test_cli_schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,12 @@ def test_validates_minutes(self):
result = runner.invoke(app, ["set", "-c", "echo foo", "-h", "8", "-m", "66"])

assert "Invalid value" in result.stdout
assert "66 is not in the valid range of 0 to 59" in result.stdout
assert "66 is not in the range 0<=x<=59" in result.stdout

def test_validates_hours(self):
result = runner.invoke(app, ["set", "-c", "echo foo", "-h", "66", "-m", "1"])
assert "Invalid value" in result.stdout
assert "66 is not in the valid range of 0 to 23" in result.stdout
assert "66 is not in the range 0<=x<=23" in result.stdout

def test_logs_warning_when_create_schedule_raises(self, mocker):
mock_logger = mocker.patch("cli.schedule.get_logger").return_value
Expand Down Expand Up @@ -347,11 +347,11 @@ def test_ensures_proper_hourly_params(self, mocker):

def test_validates_minute(self):
result = runner.invoke(app, ["update", "42", "--minute", "88"])
assert "88 is not in the valid range of 0 to 59" in result.stdout
assert "88 is not in the range 0<=x<=59" in result.stdout

def test_validates_hour(self):
result = runner.invoke(app, ["update", "42", "--daily", "--hour", "33"])
assert "33 is not in the valid range of 0 to 23" in result.stdout
assert "33 is not in the range 0<=x<=23" in result.stdout

def test_complains_when_no_id_provided(self):
result = runner.invoke(app, ["update"])
Expand Down
4 changes: 2 additions & 2 deletions tests/test_django_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,7 @@ def test_actually_produces_wsgi_file_that_can_import_project_non_nested(
running_python_version = ".".join(python_version().split(".")[:2])
project = DjangoProject("mydomain.com", running_python_version)
shutil.copytree(str(non_nested_submodule), str(project.project_path))
if running_python_version in ["3.7", "3.8"]:
if running_python_version in ["3.7", "3.8", "3.9", "3.10"]:
project.create_virtualenv(django_version="latest")
else:
project.create_virtualenv()
Expand All @@ -496,7 +496,7 @@ def test_actually_produces_wsgi_file_that_can_import_nested_project(
running_python_version = ".".join(python_version().split(".")[:2])
project = DjangoProject("mydomain.com", running_python_version)
shutil.copytree(str(more_nested_submodule), str(project.project_path))
if running_python_version in ["3.7", "3.8"]:
if running_python_version in ["3.7", "3.8", "3.9", "3.10"]:
project.create_virtualenv(django_version="latest")
else:
project.create_virtualenv()
Expand Down

0 comments on commit 96d5377

Please sign in to comment.