diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index db0fe4d..9f353f7 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -16,9 +16,8 @@ jobs:
- name: build docs
run: |
- pip install sphinx Pygments furo sphinx-copybutton sphinx-inline-tabs myst-parser colorama sphinx-panels
- cd docs
- sphinx-build -E -b html . _build
+ pip install tox
+ tox -e docs
# Runs a set of commands using the runners shell
- name: publish
diff --git a/COMMON-ISSUES.md b/COMMON-ISSUES.md
deleted file mode 100644
index 79946b1..0000000
--- a/COMMON-ISSUES.md
+++ /dev/null
@@ -1,91 +0,0 @@
-
Common Issues
-
-Context Issues
-
-On each function registered to the scheduler that requires Flask app context, assuming your `APScheduler` object is called `scheduler`, include:
-
-```python
-with scheduler.app.app_context():
- # your code
-```
-
-Mixing Persistent Jobstores with Tasks from Config
-
-When using a persistent jobstore, do not register jobs from a configuration file. They should be registered by decorators [(example)](https://github.com/viniciuschiele/flask-apscheduler/blob/master/examples/decorated.py) or by using the `add_job` method.
-
-Mixing Persistent Jobstores with Tasks in __init__.py
-
-Tasks registered via decorator or the `add_job` method should not be loaded in your `app/__init__.py` if you are using a persistent job store. If they must be loaded upon app creation, a workaround would be as follows:
-
-```python
-# app/__init__.py
-
-scheduler = APScheduler()
-db = SQLAlchemy()
-
-
-
-def create_app(config_class=Config):
- app = Flask(__name__)
- app.config.from_object(config_class)
- db.init_app(app)
- scheduler.init_app(app)
- scheduler.start()
-
- @app.before_first_request
- def load_tasks():
- from app import tasks
-
- return app
-```
-```python
-# app/tasks.py
-
-@scheduler.task('cron', id='do_renewals', hour=9, minute=5)
-def scheduled_function():
- # your scheduled task code here
-```
-
-Your task will then be registered the first time that someone makes any request to the Flask app.
-
-Trying to Load Tasks Outside Module-Level Import
-
-If your task was loading correctly with the default memory jobstore, but does not load correctly from a persistent jobstore, this is because functions to be loaded as jobs must be available as module-level imports when used with persistent jobstores. They cannot be nested within other functions or classes.
-
-So this function could be added using the `add_job` method:
-```python
-# app/tasks.py
-
-def your_function():
- # your scheduled task code here
-```
-```python
-# other_module.py
-
-scheduler.add_job()
-```
-
-You could accomplish the same by importing modules that contain decorated functions (un-nested, at the module level):
-```python
-# app/tasks.py
-
-@scheduler.task('cron', id='do_renewals', hour=9, minute=5)
-def scheduled_function():
- # your scheduled task code here
-```
-```python
-# other_module.py
-
-from app import tasks
-```
-
-But this would not work:
-```python
-# some_module.py
-
-def do_stuff():
- # do some stuff before registering a task
- # then attempt to register a task, which will fail due to nesting
- @scheduler.task('cron', id='do_renewals', hour=9, minute=5)
- def scheduled_function():
- # your scheduled task code here
diff --git a/README.rst b/README.rst
index c05f530..8a14d16 100644
--- a/README.rst
+++ b/README.rst
@@ -19,163 +19,12 @@ You can install Flask-APScheduler via Python Package Index (PyPI_),::
pip install Flask-APScheduler
-Documentation
-===============
-
-Setup
------
-
-* Create a flask application. For an example, see `this tutorial `_
-* Import and initialize ``Flask-APScheduler``
-* Set any configuration needed
-
-A basic example will looks like this.
-
-.. code-block:: python
-
- from flask import Flask
- # import Flask-APScheduler
- from flask_apscheduler import APScheduler
-
- # set configuration values
- class Config(object):
- SCHEDULER_API_ENABLED = True
-
- # create app
- app = Flask(__name__)
- app.config.from_object(Config())
-
- # initialize scheduler
- scheduler = APScheduler()
- # if you don't wanna use a config, you can set options here:
- # scheduler.api_enabled = True
- scheduler.init_app(app)
- scheduler.start()
-
-
- if __name__ == '__main__':
- app.run()
-
-Adding Jobs
------------
-
-Jobs can be added to the scheduler when the app starts. They are created in decorated functions, which should be imported before ``app.run()`` is called.
-
-.. code-block:: python
-
- # interval example
- @scheduler.task('interval', id='do_job_1', seconds=30, misfire_grace_time=900)
- def job1():
- print('Job 1 executed')
-
-
- # cron examples
- @scheduler.task('cron', id='do_job_2', minute='*')
- def job2():
- print('Job 2 executed')
-
-
- @scheduler.task('cron', id='do_job_3', week='*', day_of_week='sun')
- def job3():
- print('Job 3 executed')
-
-
-Jobs can also be added after you app is running
-
-.. code-block:: python
-
- scheduler.add_job(**args)
-
-If you wish to use anything from your Flask app context inside the job you can use something like this
-
-.. code-block:: python
-
- def blah():
- with scheduler.app.app_context():
- # do stuff
-
-Logging
--------
-All scheduler events can be used to trigger logging functions. See `APScheduler `_ for a list of available events.
-If you are using your Flask app context inside of a function triggered by a scheduler event can include something like this
-
-.. code-block:: python
-
- def blah():
- with scheduler.app.app_context():
- # do stuff
-
- scheduler.add_listener(blah, EVENT_JOB_EXECUTED | EVENT_JOB_ERROR)
-
-
-API
----
-
-Flask-APScheduler comes with a build-in API. This can be enabled/disabled in your flask configuration.
-
-.. code-block:: python
-
- SCHEDULER_API_ENABLED: True
-
-
-- /scheduler [GET] > returns basic information about the webapp
-- /scheduler/jobs [POST json job data] > adds a job to the scheduler
-- /scheduler/jobs/ [GET] > returns json of job details
-- /scheduler/jobs [GET] > returns json with details of all jobs
-- /scheduler/jobs/ [DELETE] > deletes job from scheduler
-- /scheduler/jobs/ [PATCH json job data] > updates an already existing job
-- /scheduler/jobs//pause [POST] > pauses a job, returns json of job details
-- /scheduler/jobs//resume [POST] > resumes a job, returns json of job details
-- /scheduler/jobs//run [POST] > runs a job now, returns json of job details
-
-
-Scheduler
----------
-
-Other commands can be passed to the scheduler and are rather self explainatory:
-
-- scheduler.start()
-- scheduler.shutdown()
-- scheduler.pause() > stops any job from starting. Already running jobs not affected.
-- scheduler.resume() > allows scheduled jobs to begin running.
-- scheduler.add_listener(,)
-- scheduler.remove_listener()
-- scheduler.add_job(,, **kwargs)
-- scheduler.remove_job(, **)
-- scheduler.remove_all_jobs(**)
-- scheduler.get_job(,**)
-- scheduler.modify_job(,**, **kwargs)
-- scheduler.pause_job(, **)
-- scheduler.resume_job(, **)
-- scheduler.run_job(, **)
-- scheduler.authenticate()
-
-
-Configuration
--------------
-
-Configuration options specific to ``Flask-APScheduler``:
-
-.. code-block:: python
-
- SCHEDULER_API_ENABLED:
-
-Other configuration options are included from `APScheduler `_
-
-
-Tips
-----
-
-When running Flask-APScheduler on a wsgi process only **1** worker should be enabled. APScheduler 3.0 will only work with a single worker process. Jobstores cannot be shared among multiple schedulers.
-
-See `APScheduler's `_ documentation for further help.
-
-Take a look at the examples_ to see how it works.
-
-Also take a look at `COMMON-ISSUES.md `_ for help.
+Documentation
+===============
+`See Flask APSchedulers Documentation. `_
Feedback
diff --git a/dev-requirements.txt b/dev-requirements.txt
new file mode 100644
index 0000000..ce0d807
--- /dev/null
+++ b/dev-requirements.txt
@@ -0,0 +1,6 @@
+reformat
+flake8
+flake8-bandit
+pygments
+black
+pylint
\ No newline at end of file
diff --git a/docs-requirements.txt b/docs-requirements.txt
new file mode 100644
index 0000000..ccf2f9e
--- /dev/null
+++ b/docs-requirements.txt
@@ -0,0 +1,8 @@
+sphinx
+Pygments
+furo
+sphinx-copybutton
+sphinx-inline-tabs
+myst-parser
+colorama
+sphinx-panels
\ No newline at end of file
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..d4bb2cb
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS ?=
+SPHINXBUILD ?= sphinx-build
+SOURCEDIR = .
+BUILDDIR = _build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+ @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+ @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..259988a
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,56 @@
+# flake8: noqa
+# pylint: skip-file
+# Configuration file for the Sphinx documentation builder.
+
+#
+# -- Path setup --------------------------------------------------------------
+#
+
+import sys
+from pathlib import Path
+
+sys.path.append(str(Path(__file__).parent))
+
+
+#
+# -- Project information -----------------------------------------------------
+#
+
+project = "Flask APScheduler"
+copyright = "2015, Vinicius Chiele"
+
+
+#
+# -- General configuration ---------------------------------------------------
+#
+
+extensions = [
+ "sphinx.ext.extlinks",
+ "sphinx.ext.doctest",
+ "sphinx.ext.intersphinx",
+ "sphinx.ext.viewcode",
+ "sphinx.ext.todo",
+ "sphinx_copybutton",
+ "sphinx_inline_tabs",
+ "sphinx_panels",
+ "myst_parser",
+]
+
+templates_path = ["_templates"]
+
+exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "venv"]
+
+pygments_style = "colorful"
+
+#
+# -- Options for HTML output -------------------------------------------------
+#
+
+html_theme = "furo"
+html_title = "Flask APScheduler"
+#html_logo = "images/icon-512x512.png"
+#html_favicon = "images/favicon.ico"
+
+html_theme_options = {
+ #"sidebar_hide_name": True,
+}
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..50dd468
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,22 @@
+Flask APScheduler Docs
+===========================
+
+
+.. toctree::
+ :maxdepth: 2
+ :hidden:
+ :titlesonly:
+
+ Overview
+ rst/install.rst
+ rst/configuration.rst
+ rst/usage.rst
+ rst/logging.rst
+ rst/api.rst
+ rst/examples.rst
+ rst/tips.rst
+
+
+
+.. include:: rst/readme.rst
+
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 0000000..2119f51
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,35 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+ set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=.
+set BUILDDIR=_build
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+ echo.
+ echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+ echo.installed, then set the SPHINXBUILD environment variable to point
+ echo.to the full path of the 'sphinx-build' executable. Alternatively you
+ echo.may add the Sphinx directory to PATH.
+ echo.
+ echo.If you don't have Sphinx installed, grab it from
+ echo.http://sphinx-doc.org/
+ exit /b 1
+)
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+
+:end
+popd
diff --git a/docs/placeholder b/docs/placeholder
deleted file mode 100644
index 8b13789..0000000
--- a/docs/placeholder
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/docs/rst/api.rst b/docs/rst/api.rst
new file mode 100644
index 0000000..47a1244
--- /dev/null
+++ b/docs/rst/api.rst
@@ -0,0 +1,42 @@
+***
+API
+***
+
+Flask-APScheduler comes with a build-in API. This can be enabled/disabled in your flask configuration.
+
+.. code-block:: python
+
+ SCHEDULER_API_ENABLED: True
+
+
+- /scheduler [GET] > returns basic information about the webapp
+- /scheduler/jobs [POST json job data] > adds a job to the scheduler
+- /scheduler/jobs/ [GET] > returns json of job details
+- /scheduler/jobs [GET] > returns json with details of all jobs
+- /scheduler/jobs/ [DELETE] > deletes job from scheduler
+- /scheduler/jobs/ [PATCH json job data] > updates an already existing job
+- /scheduler/jobs//pause [POST] > pauses a job, returns json of job details
+- /scheduler/jobs//resume [POST] > resumes a job, returns json of job details
+- /scheduler/jobs//run [POST] > runs a job now, returns json of job details
+
+
+Scheduler
+---------
+
+Other commands can be passed to the scheduler and are rather self explanatory:
+
+- scheduler.start()
+- scheduler.shutdown()
+- scheduler.pause() > stops any job from starting. Already running jobs not affected.
+- scheduler.resume() > allows scheduled jobs to begin running.
+- scheduler.add_listener(,)
+- scheduler.remove_listener()
+- scheduler.add_job(,, \*\*kwargs)
+- scheduler.remove_job(, \*\*)
+- scheduler.remove_all_jobs(\*\*)
+- scheduler.get_job(,\*\*)
+- scheduler.modify_job(,\*\*, \*\*kwargs)
+- scheduler.pause_job(, \*\*)
+- scheduler.resume_job(, \*\*)
+- scheduler.run_job(, \*\*)
+- scheduler.authenticate()
\ No newline at end of file
diff --git a/docs/rst/configuration.rst b/docs/rst/configuration.rst
new file mode 100644
index 0000000..dd5d7e4
--- /dev/null
+++ b/docs/rst/configuration.rst
@@ -0,0 +1,12 @@
+*************
+Configuration
+*************
+
+
+Configuration options specific to ``Flask-APScheduler``:
+
+.. code-block:: python
+
+ SCHEDULER_API_ENABLED:
+
+Other configuration options are included from `APScheduler `_
diff --git a/docs/rst/examples.rst b/docs/rst/examples.rst
new file mode 100644
index 0000000..ed738ad
--- /dev/null
+++ b/docs/rst/examples.rst
@@ -0,0 +1,14 @@
+********
+Examples
+********
+
+
+See the examples of how to use Flask-APScheduler
+
+- `Application Factory `_
+- `Advanced Job Schedules `_
+- `Allowed Hosts `_
+- `Authentication `_
+- `Decorator Usage `_
+- `Flask Context `_
+- `Jobs from Config `_
\ No newline at end of file
diff --git a/docs/rst/install.rst b/docs/rst/install.rst
new file mode 100644
index 0000000..9073ac4
--- /dev/null
+++ b/docs/rst/install.rst
@@ -0,0 +1,9 @@
+*******
+Install
+*******
+
+Installation
+===============
+You can install Flask-APScheduler via Python Package Index (`PyPi `_),::
+
+ pip install Flask-APScheduler
\ No newline at end of file
diff --git a/docs/rst/logging.rst b/docs/rst/logging.rst
new file mode 100644
index 0000000..6562af7
--- /dev/null
+++ b/docs/rst/logging.rst
@@ -0,0 +1,23 @@
+*******
+Logging
+*******
+
+
+Logging
+-------
+
+All scheduler events can be used to trigger logging functions. See `APScheduler `_ for a list of available events.
+
+If you are using your Flask app context inside of a function triggered by a scheduler event can include something like this
+
+.. code-block:: python
+
+ def blah():
+ with scheduler.app.app_context():
+ # do stuff
+
+ scheduler.add_listener(blah, EVENT_JOB_EXECUTED | EVENT_JOB_ERROR)
+
+
+
+
diff --git a/docs/rst/readme.rst b/docs/rst/readme.rst
new file mode 100644
index 0000000..408520b
--- /dev/null
+++ b/docs/rst/readme.rst
@@ -0,0 +1,55 @@
+=================================
+Flask-APScheduler
+=================================
+Flask-APScheduler is a Flask extension which adds support for the APScheduler.
+
+|Version| |Coverage| |CodeClimate| |Travis|
+
+Features
+===============
+- Loads scheduler configuration from Flask configuration.
+- Loads job definitions from Flask configuration.
+- Allows to specify the hostname which the scheduler will run on.
+- Provides a REST API to manage the scheduled jobs.
+- Provides authentication for the REST API.
+
+Installation
+===============
+You can install Flask-APScheduler via Python Package Index (PyPI_)
+
+.. code:: python
+
+ pip install Flask-APScheduler
+
+
+
+Documentation
+===============
+
+`See Flask APSchedulers Documentation. `_
+
+
+Feedback
+===============
+Please use the Issues_ for feature requests and troubleshooting usage.
+
+.. |Version| image:: https://img.shields.io/pypi/v/flask-apscheduler.svg
+ :target: https://pypi.python.org/pypi/Flask-APScheduler
+
+.. |Coverage| image:: https://codecov.io/github/viniciuschiele/flask-apscheduler/coverage.svg
+ :target: https://codecov.io/github/viniciuschiele/flask-apscheduler
+
+.. |Travis| image:: https://travis-ci.org/viniciuschiele/flask-apscheduler.svg
+ :target: https://travis-ci.org/viniciuschiele/flask-apscheduler
+
+.. |CodeClimate| image:: https://codeclimate.com/github/viniciuschiele/flask-apscheduler/badges/gpa.svg
+ :target: https://codeclimate.com/github/viniciuschiele/flask-apscheduler
+
+.. _examples: https://github.com/viniciuschiele/flask-apscheduler/tree/master/examples
+
+.. _PyPi: https://pypi.python.org/pypi/Flask-APScheduler
+
+.. _Issues: https://github.com/viniciuschiele/flask-apscheduler/issues
+
+.. _CommonIssues:
+
diff --git a/docs/rst/tips.rst b/docs/rst/tips.rst
new file mode 100644
index 0000000..9bb70d3
--- /dev/null
+++ b/docs/rst/tips.rst
@@ -0,0 +1,101 @@
+****
+Tips
+****
+
+
+When running Flask-APScheduler on a wsgi process only **1** worker should be enabled. APScheduler 3.0 will only work with a single worker process. Jobstores cannot be shared among multiple schedulers.
+
+See `APScheduler's `_ documentation for further help.
+
+Take a look at the :doc:`examples` to see how it works.
+
+
+Mixing Persistent Jobstores with Tasks from Config
+######################################################
+
+When using a persistent jobstore, do not register jobs from a configuration file. They should be registered by decorators (`see example `_), or by using the `add_job` method.
+
+
+Mixing Persistent Jobstores with Tasks in __init__.py
+######################################################
+
+Tasks registered via decorator or the `add_job` method should not be loaded in your `app/__init__.py` if you are using a persistent job store. If they must be loaded upon app creation, a workaround would be as follows:
+
+.. code:: python
+
+ # app/__init__.py
+
+ scheduler = APScheduler()
+ db = SQLAlchemy()
+
+
+
+ def create_app(config_class=Config):
+ app = Flask(__name__)
+ app.config.from_object(config_class)
+ db.init_app(app)
+ scheduler.init_app(app)
+ scheduler.start()
+
+ @app.before_first_request
+ def load_tasks():
+ from app import tasks
+
+ return app
+
+
+ # app/tasks.py
+
+ @scheduler.task('cron', id='do_renewals', hour=9, minute=5)
+ def scheduled_function():
+ # your scheduled task code here
+
+
+Your task will then be registered the first time that someone makes any request to the Flask app.
+
+Trying to Load Tasks Outside Module-Level Import
+################################################
+
+If your task was loading correctly with the default memory jobstore, but does not load correctly from a persistent jobstore, this is because functions to be loaded as jobs must be available as module-level imports when used with persistent jobstores. They cannot be nested within other functions or classes.
+
+So this function could be added using the `add_job` method:
+
+.. code:: python
+
+ # app/tasks.py
+
+ def your_function():
+ # your scheduled task code here
+
+ # other_module.py
+
+ scheduler.add_job()
+
+You could accomplish the same by importing modules that contain decorated functions (un-nested, at the module level):
+
+.. code:: python
+
+ # app/tasks.py
+
+ @scheduler.task('cron', id='do_renewals', hour=9, minute=5)
+ def scheduled_function():
+ # your scheduled task code here
+
+
+ # other_module.py
+
+ from app import tasks
+
+
+But this would not work:
+
+.. code:: python
+
+ # some_module.py
+
+ def do_stuff():
+ # do some stuff before registering a task
+ # then attempt to register a task, which will fail due to nesting
+ @scheduler.task('cron', id='do_renewals', hour=9, minute=5)
+ def scheduled_function():
+ # your scheduled task code here
diff --git a/docs/rst/usage.rst b/docs/rst/usage.rst
new file mode 100644
index 0000000..9274fdb
--- /dev/null
+++ b/docs/rst/usage.rst
@@ -0,0 +1,83 @@
+*****************
+Basic Application
+*****************
+
+Setup
+-----
+
+* Create a flask application. For an example, see `this tutorial `_
+* Import and initialize ``Flask-APScheduler``
+* Set any configuration needed
+
+A basic example will looks like this.
+
+.. code-block:: python
+
+ from flask import Flask
+ from flask_apscheduler import APScheduler
+
+ # set configuration values
+ class Config:
+ SCHEDULER_API_ENABLED = True
+
+ # create app
+ app = Flask(__name__)
+ app.config.from_object(Config())
+
+ # initialize scheduler
+ scheduler = APScheduler()
+ # if you don't wanna use a config, you can set options here:
+ # scheduler.api_enabled = True
+ scheduler.init_app(app)
+ scheduler.start()
+
+
+ if __name__ == '__main__':
+ app.run()
+
+
+Adding Jobs
+-----------
+
+Jobs can be added to the scheduler when the app starts. They are created in decorated functions, which should be imported before ``app.run()`` is called.
+
+.. code-block:: python
+
+ # interval example
+ @scheduler.task('interval', id='do_job_1', seconds=30, misfire_grace_time=900)
+ def job1():
+ print('Job 1 executed')
+
+
+ # cron examples
+ @scheduler.task('cron', id='do_job_2', minute='*')
+ def job2():
+ print('Job 2 executed')
+
+
+ @scheduler.task('cron', id='do_job_3', week='*', day_of_week='sun')
+ def job3():
+ print('Job 3 executed')
+
+
+ scheduler.start()
+
+
+Jobs can also be added after you app is running
+
+.. code-block:: python
+
+ scheduler.start()
+ scheduler.add_job(**args)
+
+
+Flask Context
+-------------
+
+If you wish to use anything from your Flask app context inside the job you can use something like this
+
+.. code-block:: python
+
+ def blah():
+ with scheduler.app.app_context():
+ # do stuff
diff --git a/examples/advanced.py b/examples/advanced.py
index 3d80d6c..13b7a93 100644
--- a/examples/advanced.py
+++ b/examples/advanced.py
@@ -1,40 +1,43 @@
+"""Advanced example using other configuration options."""
+
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from flask import Flask
+
from flask_apscheduler import APScheduler
-class Config(object):
+class Config:
+ """App configuration."""
+
JOBS = [
{
- 'id': 'job1',
- 'func': 'advanced:job1',
- 'args': (1, 2),
- 'trigger': 'interval',
- 'seconds': 10
+ "id": "job1",
+ "func": "advanced:job1",
+ "args": (1, 2),
+ "trigger": "interval",
+ "seconds": 10,
}
]
- SCHEDULER_JOBSTORES = {
- 'default': SQLAlchemyJobStore(url='sqlite://')
- }
+ SCHEDULER_JOBSTORES = {"default": SQLAlchemyJobStore(url="sqlite://")}
- SCHEDULER_EXECUTORS = {
- 'default': {'type': 'threadpool', 'max_workers': 20}
- }
+ SCHEDULER_EXECUTORS = {"default": {"type": "threadpool", "max_workers": 20}}
- SCHEDULER_JOB_DEFAULTS = {
- 'coalesce': False,
- 'max_instances': 3
- }
+ SCHEDULER_JOB_DEFAULTS = {"coalesce": False, "max_instances": 3}
SCHEDULER_API_ENABLED = True
-def job1(a, b):
- print(str(a) + ' ' + str(b))
+def job1(var_one, var_two):
+ """Demo job function.
+
+ :param var_two:
+ :param var_two:
+ """
+ print(str(var_one) + " " + str(var_two))
-if __name__ == '__main__':
+if __name__ == "__main__":
app = Flask(__name__)
app.config.from_object(Config())
diff --git a/examples/allowed_host.py b/examples/allowed_host.py
index 8e3138d..b4f7171 100644
--- a/examples/allowed_host.py
+++ b/examples/allowed_host.py
@@ -1,33 +1,43 @@
+"""Allowed hosts example."""
+
from flask import Flask
+
from flask_apscheduler import APScheduler
-class Config(object):
+class Config:
+ """App configuration."""
+
JOBS = [
{
- 'id': 'job1',
- 'func': 'allowed_host:job1',
- 'args': (1, 2),
- 'trigger': 'interval',
- 'seconds': 10
+ "id": "job1",
+ "func": "allowed_host:job1",
+ "args": (1, 2),
+ "trigger": "interval",
+ "seconds": 10,
}
]
- SCHEDULER_ALLOWED_HOSTS = ['my_servers_name']
+ SCHEDULER_ALLOWED_HOSTS = ["my_servers_name"]
SCHEDULER_API_ENABLED = True
-def job1(a, b):
- print(str(a) + ' ' + str(b))
+def job1(var_one, var_two):
+ """Demo job function.
+
+ :param var_two:
+ :param var_two:
+ """
+ print(str(var_one) + " " + str(var_two))
-if __name__ == '__main__':
+if __name__ == "__main__":
app = Flask(__name__)
app.config.from_object(Config())
scheduler = APScheduler()
# it is also possible to set the list of servers directly
- # scheduler.allowed_hosts = ['my_servers_name']
+ # scheduler.allowed_hosts = ['my_servers_name'] # noqa: E800
scheduler.init_app(app)
scheduler.start()
diff --git a/examples/application_factory/__init__.py b/examples/application_factory/__init__.py
new file mode 100644
index 0000000..ec23e54
--- /dev/null
+++ b/examples/application_factory/__init__.py
@@ -0,0 +1,64 @@
+"""Flask APScheduler example using Flask's Application Factory setup.
+
+Run using:
+
+.. code ::python
+
+ pip install flask-apscheduler
+ export FLASK_ENV=development && export FLASK_DEBUG=1 && export FLASK_APP=__init__ && flask run
+
+"""
+
+import logging
+import os
+
+from flask import Flask
+
+from .extensions import scheduler
+from .settings import DevelopmentConfig
+
+
+def create_app():
+ """Create a new app instance."""
+
+ def is_debug_mode():
+ """Get app debug status."""
+ debug = os.environ.get("FLASK_DEBUG")
+ if not debug:
+ return os.environ.get("FLASK_ENV") == "development"
+ return debug.lower() not in ("0", "false", "no")
+
+ def is_werkzeug_reloader_process():
+ """Get werkzeug status."""
+ return os.environ.get("WERKZEUG_RUN_MAIN") == "true"
+
+ # pylint: disable=W0621
+ app = Flask(__name__)
+
+ app.config.from_object(DevelopmentConfig)
+ scheduler.init_app(app)
+
+ logging.getLogger("apscheduler").setLevel(logging.INFO)
+
+ # pylint: disable=C0415, W0611
+ with app.app_context():
+
+ # pylint: disable=W0611
+ if is_debug_mode() and not is_werkzeug_reloader_process():
+ pass
+ else:
+ from . import tasks # noqa: F401
+
+ scheduler.start()
+
+ from . import events, web # noqa: F401
+
+ app.register_blueprint(web.web_bp)
+
+ return app
+
+
+app = create_app()
+
+if __name__ == "__main__":
+ app.run()
diff --git a/examples/application_factory/events.py b/examples/application_factory/events.py
new file mode 100644
index 0000000..b820177
--- /dev/null
+++ b/examples/application_factory/events.py
@@ -0,0 +1,57 @@
+"""Log scheduler events."""
+
+
+from apscheduler.events import (
+ EVENT_JOB_ADDED,
+ EVENT_JOB_ERROR,
+ EVENT_JOB_EXECUTED,
+ EVENT_JOB_MISSED,
+ EVENT_JOB_REMOVED,
+ EVENT_JOB_SUBMITTED,
+)
+
+from .extensions import scheduler
+
+
+def job_missed(event):
+ """Job missed event."""
+ with scheduler.app.app_context():
+ print(event) # noqa: T001
+
+
+def job_error(event):
+ """Job error event."""
+ with scheduler.app.app_context():
+ print(event) # noqa: T001
+
+
+def job_executed(event):
+ """Job executed event."""
+ with scheduler.app.app_context():
+ print(event) # noqa: T001
+
+
+def job_added(event):
+ """Job added event."""
+ with scheduler.app.app_context():
+ print(event) # noqa: T001
+
+
+def job_removed(event):
+ """Job removed event."""
+ with scheduler.app.app_context():
+ print(event) # noqa: T001
+
+
+def job_submitted(event):
+ """Job scheduled to run event."""
+ with scheduler.app.app_context():
+ print(event) # noqa: T001
+
+
+scheduler.add_listener(job_missed, EVENT_JOB_MISSED)
+scheduler.add_listener(job_error, EVENT_JOB_ERROR)
+scheduler.add_listener(job_executed, EVENT_JOB_EXECUTED)
+scheduler.add_listener(job_added, EVENT_JOB_ADDED)
+scheduler.add_listener(job_removed, EVENT_JOB_REMOVED)
+scheduler.add_listener(job_submitted, EVENT_JOB_SUBMITTED)
diff --git a/examples/application_factory/extensions.py b/examples/application_factory/extensions.py
new file mode 100644
index 0000000..1a0ab06
--- /dev/null
+++ b/examples/application_factory/extensions.py
@@ -0,0 +1,7 @@
+"""Initialize any app extensions."""
+
+from flask_apscheduler import APScheduler
+
+scheduler = APScheduler()
+
+# ... any other stuff.. db, caching, sessions, etc.
diff --git a/examples/application_factory/settings.py b/examples/application_factory/settings.py
new file mode 100644
index 0000000..9e11a05
--- /dev/null
+++ b/examples/application_factory/settings.py
@@ -0,0 +1,14 @@
+"""App configuration."""
+
+
+class Config:
+ """Prod config."""
+
+ DEBUG = False
+ TESTING = False
+
+
+class DevelopmentConfig(Config):
+ """Dev config."""
+
+ DEBUG = True
diff --git a/examples/application_factory/tasks.py b/examples/application_factory/tasks.py
new file mode 100644
index 0000000..f940876
--- /dev/null
+++ b/examples/application_factory/tasks.py
@@ -0,0 +1,30 @@
+"""Example of adding tasks on app startup."""
+
+from .extensions import scheduler
+
+
+@scheduler.task(
+ "interval",
+ id="job_sync",
+ seconds=10,
+ max_instances=1,
+ start_date="2000-01-01 12:19:00",
+)
+def task1():
+ """Sample task 1.
+
+ Added when app starts.
+ """
+ print("running task 1!") # noqa: T001
+
+ # oh, do you need something from config?
+ with scheduler.app.app_context():
+ print(scheduler.app.config) # noqa: T001
+
+
+def task2():
+ """Sample task 2.
+
+ Added when /add url is visited.
+ """
+ print("running task 2!") # noqa: T001
diff --git a/examples/application_factory/web.py b/examples/application_factory/web.py
new file mode 100644
index 0000000..d1e28a8
--- /dev/null
+++ b/examples/application_factory/web.py
@@ -0,0 +1,37 @@
+"""Example web view for application factory."""
+
+
+from flask import Blueprint
+
+from .extensions import scheduler
+from .tasks import task2
+
+web_bp = Blueprint("web_bp", __name__)
+
+
+@web_bp.route("/")
+def index():
+ """Say hi!.
+
+ :url: /
+ :returns: hi!
+ """
+ return "hi!"
+
+
+@web_bp.route("/add")
+def add():
+ """Add a task.
+
+ :url: /add/
+ :returns: job
+ """
+ job = scheduler.add_job(
+ func=task2,
+ trigger="interval",
+ seconds=10,
+ id="test job 2",
+ name="test job 2",
+ replace_existing=True,
+ )
+ return "%s added!" % job.name
diff --git a/examples/auth.py b/examples/auth.py
index bab8532..fa5ef05 100644
--- a/examples/auth.py
+++ b/examples/auth.py
@@ -1,16 +1,21 @@
+"""Authorization example."""
+
from flask import Flask
+
from flask_apscheduler import APScheduler
from flask_apscheduler.auth import HTTPBasicAuth
-class Config(object):
+class Config:
+ """App configuration."""
+
JOBS = [
{
- 'id': 'job1',
- 'func': '__main__:job1',
- 'args': (1, 2),
- 'trigger': 'interval',
- 'seconds': 10
+ "id": "job1",
+ "func": "__main__:job1",
+ "args": (1, 2),
+ "trigger": "interval",
+ "seconds": 10,
}
]
@@ -18,22 +23,28 @@ class Config(object):
SCHEDULER_AUTH = HTTPBasicAuth()
-def job1(a, b):
- print(str(a) + ' ' + str(b))
+def job1(var_one, var_two):
+ """Demo job function.
+
+ :param var_two:
+ :param var_two:
+ """
+ print(str(var_one) + " " + str(var_two))
-if __name__ == '__main__':
+if __name__ == "__main__":
app = Flask(__name__)
app.config.from_object(Config())
scheduler = APScheduler()
# it is also possible to set the authentication directly
- # scheduler.auth = HTTPBasicAuth()
+ # scheduler.auth = HTTPBasicAuth() # noqa: E800
scheduler.init_app(app)
scheduler.start()
@scheduler.authenticate
def authenticate(auth):
- return auth['username'] == 'guest' and auth['password'] == 'guest'
+ """Check auth."""
+ return auth["username"] == "guest" and auth["password"] == "guest"
app.run()
diff --git a/examples/decorated.py b/examples/decorated.py
index b54ee7c..5fb003b 100644
--- a/examples/decorated.py
+++ b/examples/decorated.py
@@ -1,8 +1,12 @@
+"""Example of decorators."""
from flask import Flask
+
from flask_apscheduler import APScheduler
-class Config(object):
+class Config:
+ """App configuration."""
+
SCHEDULER_API_ENABLED = True
@@ -10,28 +14,31 @@ class Config(object):
# interval examples
-@scheduler.task('interval', id='do_job_1', seconds=30, misfire_grace_time=900)
+@scheduler.task("interval", id="do_job_1", seconds=30, misfire_grace_time=900)
def job1():
- print('Job 1 executed')
+ """Sample job 1."""
+ print("Job 1 executed")
# cron examples
-@scheduler.task('cron', id='do_job_2', minute='*')
+@scheduler.task("cron", id="do_job_2", minute="*")
def job2():
- print('Job 2 executed')
+ """Sample job 2."""
+ print("Job 2 executed")
-@scheduler.task('cron', id='do_job_3', week='*', day_of_week='sun')
+@scheduler.task("cron", id="do_job_3", week="*", day_of_week="sun")
def job3():
- print('Job 3 executed')
+ """Sample job 3."""
+ print("Job 3 executed")
-if __name__ == '__main__':
+if __name__ == "__main__":
app = Flask(__name__)
app.config.from_object(Config())
# it is also possible to enable the API directly
- # scheduler.api_enabled = True
+ # scheduler.api_enabled = True # noqa: E800
scheduler.init_app(app)
scheduler.start()
diff --git a/examples/flask_context.py b/examples/flask_context.py
index a404fc4..dda80d0 100644
--- a/examples/flask_context.py
+++ b/examples/flask_context.py
@@ -1,41 +1,41 @@
+"""Example using flask context."""
+
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from flask import Flask
-from flask_apscheduler import APScheduler
from flask_sqlalchemy import SQLAlchemy
+from flask_apscheduler import APScheduler
db = SQLAlchemy()
class User(db.Model):
- id = db.Column(db.Integer, primary_key=True)
+ """User model."""
+
+ id = db.Column(db.Integer, primary_key=True) # noqa: A003, VNE003
username = db.Column(db.String(80), unique=True)
email = db.Column(db.String(120), unique=True)
def show_users():
+ """Print all users."""
with db.app.app_context():
print(User.query.all())
-class Config(object):
- JOBS = [
- {
- 'id': 'job1',
- 'func': show_users,
- 'trigger': 'interval',
- 'seconds': 2
- }
- ]
+class Config:
+ """App configuration."""
+
+ JOBS = [{"id": "job1", "func": show_users, "trigger": "interval", "seconds": 2}]
SCHEDULER_JOBSTORES = {
- 'default': SQLAlchemyJobStore(url='sqlite:///flask_context.db')
+ "default": SQLAlchemyJobStore(url="sqlite:///flask_context.db")
}
SCHEDULER_API_ENABLED = True
-if __name__ == '__main__':
+if __name__ == "__main__":
app = Flask(__name__)
app.config.from_object(Config())
diff --git a/examples/jobs.py b/examples/jobs.py
index f718417..fc8a864 100644
--- a/examples/jobs.py
+++ b/examples/jobs.py
@@ -1,32 +1,43 @@
+"""Basic Flask Example."""
+
+
from flask import Flask
+
from flask_apscheduler import APScheduler
-class Config(object):
+class Config:
+ """App configuration."""
+
JOBS = [
{
- 'id': 'job1',
- 'func': 'jobs:job1',
- 'args': (1, 2),
- 'trigger': 'interval',
- 'seconds': 10
+ "id": "job1",
+ "func": "jobs:job1",
+ "args": (1, 2),
+ "trigger": "interval",
+ "seconds": 10,
}
]
SCHEDULER_API_ENABLED = True
-def job1(a, b):
- print(str(a) + ' ' + str(b))
+def job1(var_one, var_two):
+ """Demo job function.
+
+ :param var_two:
+ :param var_two:
+ """
+ print(str(var_one) + " " + str(var_two))
-if __name__ == '__main__':
+if __name__ == "__main__":
app = Flask(__name__)
app.config.from_object(Config())
scheduler = APScheduler()
# it is also possible to enable the API directly
- # scheduler.api_enabled = True
+ # scheduler.api_enabled = True # noqa: E800
scheduler.init_app(app)
scheduler.start()
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..3b10d77
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,14 @@
+[flake8]
+max-line-length = 99
+exclude=
+ temp,
+ .git,
+ .gitignore,
+ *.pot,
+ *.py[co],
+ __pycache__,
+ venv,
+ .env,
+ .venv
+ flask-apscheduler
+ tests
\ No newline at end of file
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..325a231
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,45 @@
+
+[tox]
+envlist =
+ py37,
+ lint,
+ docs,
+skip_missing_interpreters = True
+isolated_build = True
+
+
+[testenv]
+description = test
+basepython = python3.7
+deps =
+ -rrequirements.txt
+ -rtest-requirements.txt
+commands =
+ nosetests -v -l DEBUG --logging-level=DEBUG --with-coverage --cover-package=flask_apscheduler
+skip_install = true
+
+[testenv:lint]
+basepython = python3.7
+description = check code style
+deps =
+ -rdev-requirements.txt
+commands =
+ black examples
+ isort examples --profile="black"
+ flake8 examples
+ pylint examples
+skip_install = true
+
+
+[testenv:docs]
+basepython = python3.7
+description = update documentation
+changedir = docs
+deps =
+ -rdocs-requirements.txt
+commands = sphinx-build -E -b html . _build
+skip_install = true
+setenv =
+ PYTHONDONTWRITEBYTECODE=1
+ DEBUG=False
+