From 383c4f2da779cf596a8fe645a1c3d8c1db5d672e Mon Sep 17 00:00:00 2001 From: Matthew Bourque Date: Tue, 21 Nov 2017 16:35:06 -0500 Subject: [PATCH 01/11] Initial commit of style guide. --- style_guide.md | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 style_guide.md diff --git a/style_guide.md b/style_guide.md new file mode 100644 index 000000000..f873cfc7d --- /dev/null +++ b/style_guide.md @@ -0,0 +1,60 @@ +Python Code Style Guide for `jwql` +================================= + +This document serves as a style guide for all `jwql` software development. Any requested contribution to the `jwql` code repository should be checked against this guide, and any violoation of the guide should be fixed before the code is committed to +the `master` branch. Please refer to the accompanying `example.py` script for a example code that abides by this style guide. + +Prerequisite Reading +-------------------- + +It is assumed that the reader of this style guide has read and is familiar with the following: + +- The [PEP8 Style Guide for Python Code](https://www.python.org/dev/peps/pep-0008/) +- The [PEP257 Docstring Conventions Style Guide](https://www.python.org/dev/peps/pep-0257/) +- The [`numpydoc` docstring convention](https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt) + + +Workflow +-------- + +All software development for the `jwql` project should follow a continuious integration workflow which resembles the following: + +1. Open a GitHub issue - The GitHub issue should have an adequate description of the proposed feature, bug, or question. Any appropriate individuals should be assinged to the issue, and a label(s), project(s) and/or milestone(s) should be tagged. +2. Create a branch in which to perform the software development. Branch names should be short but descriptive (e.g. `new-database-table` or `fix-ingest-algorithm`) and not too generic (e.g. `bug-fix`). Also consistent use of hyphens is encouraged. +3. Before commiting any code changes, use `flake8` to check the code against `PEP8` standards. Also check that your code is conforming to this style guide. +4. Commit changes to the branch and push the branch to the remote repository. +5. Create a new pull request that compares the new branch to the target branch (usually `master`, unless one is branching off of another branch). +6. Request a reviewer on the pull request. +7. The reviewer checks the pull request against this style guide and proposes changes if necessary. +8. The author and reviewer iterate on changes until everyone is satisfied. +9. The reviewer accepts the pull request, merges the branch, and deletes the branch unless asked not to. +10. The author comments on the original GitHub issue with link to appropriate pull request and closes the issue if satisfied. + + +Version Numbers and Tags +------------------------ + +Any changes pushed to the `master` branch should be tagged with a version number. The version number convention is `x.y.z`, where + + x = The main version number. Increase when making incompatible API changes. + y = The feature number. Increase when change contains a new feature with or without bug fixes. + z = The hotfix number. Increase when change only contains bug fixes. + + +`jwql`-specific Code Standards +------------------------------ + +`jwql` code shall adhere to the `PEP8` conventions save for the following exceptions: + + - Lines of code need not to be restricted to 79 characters. However, it is encouraged to break up obnoxiously long lines into several lines if it benefits the overall readability of the code + - + + +`jwql`-Specific Documentation Standards +--------------------------------------- + +`jwql` code shall adhere to the `PEP257` and `numpydoc` conventions. The following are further reccomendations: + +- Each module should have at minimum a description, `Authors`, `Use`, and `Dependencies` seciton. +- Each function/method should have at minimum a description, `Parameters` (if necessary), and `Returns` (if necessary) sections +- \ No newline at end of file From fb8be12892362208fd74d7e3007db20f4be2812b Mon Sep 17 00:00:00 2001 From: Matthew Bourque Date: Tue, 21 Nov 2017 18:25:07 -0500 Subject: [PATCH 02/11] Initial commit of example.py module. --- example.py | 165 +++++++++++++++++++++++++++++++++++++++++++++++++ style_guide.md | 7 ++- 2 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 example.py diff --git a/example.py b/example.py new file mode 100644 index 000000000..eb74cd865 --- /dev/null +++ b/example.py @@ -0,0 +1,165 @@ +#! /usr/bin/env python +"""This is the module docstring. + +The module docstring should have a one line description (as above) as +well as a more detailed description in a paragraph below the one line +description (i.e. this). Module dosctring lines should be limited to +72 characters. Monospace font can be achived with ``two single +forward-apostrophes``. The description should provided a detailed +overview of the purpose of the module (what does it do) and how it +acheieves this purpose (how does it do it). + +Authors +------- + + - Matthew Bourque + +Use +--- + + This module can be executed via the command line as such: + + :: + python example.py [path] [-f|--filter filter] + + Required arguments: + + ``path`` - The path to the input file + + Optional arguments: + + ``-f|--filter`` - The filter to process. if not provided, the + defult value is "F606W". + +Dependencies +------------ + + Here is where any external dependencies can be listed or described. + For example: + + The user must have a configuration file named ``config.yaml`` + placed in the current working directory. + +References +---------- + + Here is where any references to external sources related to the + code can be listed or described. For example: + + Code adopted from IDL routine written by Hilbert et al., 2009. + +Notes +----- + + Here is where any additional notes (that are beyond the scope of the + description) can be described. +""" + +import argparse +import glob +import os +import sys + +from astropy.io import fits +import matplotlib.pyplot as plt +import numpy as np +import scipy +from sqlalchemy import Float, Integer, String + + +def my_main_function(path, filter): + """The main function of the ``example`` module. + + This function performs the main tasks of the module. See module + docstrings for further details. + + Parameters + ---------- + path : str + The path to the input file. + filter : str + The filter to process (e.g. "F606W"). + """ + + print('Using {} as an input file'.format(path)) + an_int = 1 + a_float = 3.14 + a_bool = True + a_list = ['Dog', 'Cat', 'Turtle', False, 7] + a_tuple = ('Dog', 'Cat', 'Turtle', False, 7) + a_dict = {'key1': 'value1', 'key2': 'value2'} + an_obj = object() + + result = some_other_function(an_int, a_float, a_bool, a_list, a_tuple, a_dict, an_obj) + + print(result) + + +def parse_args(): + """Parse command line arguments. Returns ``args`` object. + + Returns + ------- + args : obj + An argparse object containing all of the arguments + """ + + # Create help strings + path_help = 'The path to the input file.' + filter_help = 'The filter to process (e.g. "F606W").' + + # Add arguments + parser = argparse.ArgumentParser() + parser.add_argument('path', + type=str, + default=os.getcwd(), + help=path_help) + parser.add_argument('-f --filter', + dest='filter', + type=str, + required=False, + default='F606W', + help=filter_help) + + # Parse args + args = parser.parse_args() + + return args + + +def some_other_function(an_int, a_float, a_bool, a_list, a_tuple, a_dict, an_obj): + """This function just does a bunch of nonsense. + + But it serves as a decent example of some things. + + Parameters + ---------- + an_int : int + Who knows what we will use this integer for. + a_bool : bool + Who knows what we will use this boolean for. + a_float: float + Who knows what we will use this float for. + a_list : list + Who knows what we will use this list for. + a_tuple : tuple + Who knows what we will use this tuple for. + a_dict : dict + Who knows what we will use this dictionary for. + an_obj : obj + Who knows what we will use this object for. + + Returns + ------- + results : int + The result of the function. + """ + + pass + + +if __name__ == '__main__': + + args = parse_args() + + my_main_function(args.path, args.filter) diff --git a/style_guide.md b/style_guide.md index f873cfc7d..9d7c1c750 100644 --- a/style_guide.md +++ b/style_guide.md @@ -49,12 +49,17 @@ Any changes pushed to the `master` branch should be tagged with a version number - Lines of code need not to be restricted to 79 characters. However, it is encouraged to break up obnoxiously long lines into several lines if it benefits the overall readability of the code - + Additionally, the code shall adhere to the following guidelines: + + - Function and class definitions should be placed in alphabetical order in the module + - + `jwql`-Specific Documentation Standards --------------------------------------- `jwql` code shall adhere to the `PEP257` and `numpydoc` conventions. The following are further reccomendations: -- Each module should have at minimum a description, `Authors`, `Use`, and `Dependencies` seciton. +- Each module should have at minimum a description, `Authors` and `Use` seciton. - Each function/method should have at minimum a description, `Parameters` (if necessary), and `Returns` (if necessary) sections - \ No newline at end of file From b6149a6c41a2afc65298828cff9e49de8c86cbf4 Mon Sep 17 00:00:00 2001 From: Matthew Bourque Date: Thu, 7 Dec 2017 14:44:09 -0500 Subject: [PATCH 03/11] First draft of style guide --- style_guide.md | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/style_guide.md b/style_guide.md index 9d7c1c750..72c6e067f 100644 --- a/style_guide.md +++ b/style_guide.md @@ -1,7 +1,7 @@ Python Code Style Guide for `jwql` ================================= -This document serves as a style guide for all `jwql` software development. Any requested contribution to the `jwql` code repository should be checked against this guide, and any violoation of the guide should be fixed before the code is committed to +This document serves as a style guide for all `jwql` software development. Any requested contribution to the `jwql` code repository should be checked against this guide, and any violation of the guide should be fixed before the code is committed to the `master` branch. Please refer to the accompanying `example.py` script for a example code that abides by this style guide. Prerequisite Reading @@ -17,11 +17,11 @@ It is assumed that the reader of this style guide has read and is familiar with Workflow -------- -All software development for the `jwql` project should follow a continuious integration workflow which resembles the following: +All software development for the `jwql` project should follow a continuous integration workflow which resembles the following: -1. Open a GitHub issue - The GitHub issue should have an adequate description of the proposed feature, bug, or question. Any appropriate individuals should be assinged to the issue, and a label(s), project(s) and/or milestone(s) should be tagged. +1. Open a GitHub issue - The GitHub issue should have an adequate description of the proposed feature, bug, or question. Any appropriate individuals should be assigned to the issue, and a label(s), project(s) and/or milestone(s) should be tagged. 2. Create a branch in which to perform the software development. Branch names should be short but descriptive (e.g. `new-database-table` or `fix-ingest-algorithm`) and not too generic (e.g. `bug-fix`). Also consistent use of hyphens is encouraged. -3. Before commiting any code changes, use `flake8` to check the code against `PEP8` standards. Also check that your code is conforming to this style guide. +3. Before committing any code changes, use `flake8` to check the code against `PEP8` standards. Also check that your code is conforming to this style guide. 4. Commit changes to the branch and push the branch to the remote repository. 5. Create a new pull request that compares the new branch to the target branch (usually `master`, unless one is branching off of another branch). 6. Request a reviewer on the pull request. @@ -41,25 +41,35 @@ Any changes pushed to the `master` branch should be tagged with a version number z = The hotfix number. Increase when change only contains bug fixes. +Security +-------- + +The following items should never be committed in the `jwql` source code or GitHub issues/pull requests: + +- Account credentials of any kind (e.g. database usernames and passwords) +- Internal directory structures or filepaths +- Machine names +- Proprietary data + +If `jwql` code needs to be aware of this information, it should be stored in a configuration file that is not part of the `jwql` repository. + + `jwql`-specific Code Standards ------------------------------ `jwql` code shall adhere to the `PEP8` conventions save for the following exceptions: - Lines of code need not to be restricted to 79 characters. However, it is encouraged to break up obnoxiously long lines into several lines if it benefits the overall readability of the code - - - Additionally, the code shall adhere to the following guidelines: + Additionally, the code shall adhere to the following special guidelines: - Function and class definitions should be placed in alphabetical order in the module - - `jwql`-Specific Documentation Standards --------------------------------------- -`jwql` code shall adhere to the `PEP257` and `numpydoc` conventions. The following are further reccomendations: +`jwql` code shall adhere to the `PEP257` and `numpydoc` conventions. The following are further recommendations: -- Each module should have at minimum a description, `Authors` and `Use` seciton. +- Each module should have at minimum a description, `Authors` and `Use` section. - Each function/method should have at minimum a description, `Parameters` (if necessary), and `Returns` (if necessary) sections -- \ No newline at end of file From 2f066d1605e128d6e44f164d94f96055d17de98d Mon Sep 17 00:00:00 2001 From: Sara Ogaz Date: Mon, 11 Dec 2017 16:44:06 -0500 Subject: [PATCH 04/11] Update style_guide.md --- style_guide.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/style_guide.md b/style_guide.md index 72c6e067f..58fc0c95f 100644 --- a/style_guide.md +++ b/style_guide.md @@ -26,9 +26,9 @@ All software development for the `jwql` project should follow a continuous integ 5. Create a new pull request that compares the new branch to the target branch (usually `master`, unless one is branching off of another branch). 6. Request a reviewer on the pull request. 7. The reviewer checks the pull request against this style guide and proposes changes if necessary. -8. The author and reviewer iterate on changes until everyone is satisfied. +8. The author and reviewer iterate on changes until everyone is satisfied. The author can now squash commits if neccesary/desired. 9. The reviewer accepts the pull request, merges the branch, and deletes the branch unless asked not to. -10. The author comments on the original GitHub issue with link to appropriate pull request and closes the issue if satisfied. +10. The author comments on the original GitHub issue with link to appropriate pull request and closes the issue if satisfied. (This can also be done by adding a "closes #3" in the original commit message, and the issue will be closed when that commit is merged into master. Version Numbers and Tags From 1669c3c78feec483adb3dd13213a2b66896b153d Mon Sep 17 00:00:00 2001 From: Matthew Bourque Date: Tue, 19 Dec 2017 14:20:43 -0500 Subject: [PATCH 05/11] Added some additional code to example.py --- example.py | 14 +++++++++++++- style_guide.md | 32 +++++++++++++++++++++----------- 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/example.py b/example.py index eb74cd865..4003d0d2d 100644 --- a/example.py +++ b/example.py @@ -67,6 +67,11 @@ from sqlalchemy import Float, Integer, String +# Global variables should be avioded, but if used should be named with +# all-caps +A_GLOBAL_VARIABLE = 'foo' + + def my_main_function(path, filter): """The main function of the ``example`` module. @@ -155,7 +160,14 @@ def some_other_function(an_int, a_float, a_bool, a_list, a_tuple, a_dict, an_obj The result of the function. """ - pass + # File I/O should be handeled with 'with open' when possible + with open('my_file', 'w') as f: + f.write('My favorite integer is {}'.format(an_int)) + + # Operators should be separated by spaces + print(a_float + a_float) + + if __name__ == '__main__': diff --git a/style_guide.md b/style_guide.md index 9d7c1c750..95aecd553 100644 --- a/style_guide.md +++ b/style_guide.md @@ -1,7 +1,7 @@ Python Code Style Guide for `jwql` ================================= -This document serves as a style guide for all `jwql` software development. Any requested contribution to the `jwql` code repository should be checked against this guide, and any violoation of the guide should be fixed before the code is committed to +This document serves as a style guide for all `jwql` software development. Any requested contribution to the `jwql` code repository should be checked against this guide, and any violation of the guide should be fixed before the code is committed to the `master` branch. Please refer to the accompanying `example.py` script for a example code that abides by this style guide. Prerequisite Reading @@ -17,11 +17,11 @@ It is assumed that the reader of this style guide has read and is familiar with Workflow -------- -All software development for the `jwql` project should follow a continuious integration workflow which resembles the following: +All software development for the `jwql` project should follow a continuous integration workflow which resembles the following: -1. Open a GitHub issue - The GitHub issue should have an adequate description of the proposed feature, bug, or question. Any appropriate individuals should be assinged to the issue, and a label(s), project(s) and/or milestone(s) should be tagged. +1. Open a GitHub issue - The GitHub issue should have an adequate description of the proposed feature, bug, or question. Any appropriate individuals should be assigned to the issue, and a label(s), project(s) and/or milestone(s) should be tagged. 2. Create a branch in which to perform the software development. Branch names should be short but descriptive (e.g. `new-database-table` or `fix-ingest-algorithm`) and not too generic (e.g. `bug-fix`). Also consistent use of hyphens is encouraged. -3. Before commiting any code changes, use `flake8` to check the code against `PEP8` standards. Also check that your code is conforming to this style guide. +3. Before committing any code changes, use `flake8` to check the code against `PEP8` standards. Also check that your code is conforming to this style guide. 4. Commit changes to the branch and push the branch to the remote repository. 5. Create a new pull request that compares the new branch to the target branch (usually `master`, unless one is branching off of another branch). 6. Request a reviewer on the pull request. @@ -41,25 +41,35 @@ Any changes pushed to the `master` branch should be tagged with a version number z = The hotfix number. Increase when change only contains bug fixes. +Security +-------- + +The following items should never be committed in the `jwql` source code or GitHub issues/pull requests: + +- Account credentials of any kind (e.g. database usernames and passwords) +- Internal directory structures or filepaths +- Machine names +- Proprietary data + +If `jwql` code needs to be aware of this information, it should be stored in a configuration file that is not part of the `jwql` repository. + + `jwql`-specific Code Standards ------------------------------ `jwql` code shall adhere to the `PEP8` conventions save for the following exceptions: - Lines of code need not to be restricted to 79 characters. However, it is encouraged to break up obnoxiously long lines into several lines if it benefits the overall readability of the code - - - Additionally, the code shall adhere to the following guidelines: + Additionally, the code shall adhere to the following special guidelines: - Function and class definitions should be placed in alphabetical order in the module - - `jwql`-Specific Documentation Standards --------------------------------------- -`jwql` code shall adhere to the `PEP257` and `numpydoc` conventions. The following are further reccomendations: +`jwql` code shall adhere to the `PEP257` and `numpydoc` conventions. The following are further recommendations: -- Each module should have at minimum a description, `Authors` and `Use` seciton. -- Each function/method should have at minimum a description, `Parameters` (if necessary), and `Returns` (if necessary) sections -- \ No newline at end of file +- Each module should have at minimum a description, `Authors` and `Use` section. +- Each function/method should have at minimum a description, `Parameters` (if necessary), and `Returns` (if necessary) sections \ No newline at end of file From 1d599492832ecaaef1b1e3dc13d8ce7c162ff9ee Mon Sep 17 00:00:00 2001 From: Matthew Bourque Date: Wed, 20 Dec 2017 13:29:08 -0500 Subject: [PATCH 06/11] Initial commit of PEP8_examples notebook --- .gitignore | 3 +- PEP8_examples.ipynb | 802 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 804 insertions(+), 1 deletion(-) create mode 100644 PEP8_examples.ipynb diff --git a/.gitignore b/.gitignore index 47d7f9a77..25864d7c1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.pyc -*.fits \ No newline at end of file +*.fits +*.ipynb_checkpoints/ \ No newline at end of file diff --git a/PEP8_examples.ipynb b/PEP8_examples.ipynb new file mode 100644 index 000000000..3f442c0fa --- /dev/null +++ b/PEP8_examples.ipynb @@ -0,0 +1,802 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# `PEP 8` Style Guide Examples" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This notebook aims to condense and illustrate the examples given in the the [`PEP 8` Style Guide for Python](https://www.python.org/dev/peps/pep-0008/) in notebook form. The text and examples in this notebook are mostly taken straight from the `PEP 8` document, but some areas are supplemented with items specific to `jwql` development. Sections of `PEP 8` that are not particularly relevant to the `jwql` project are ignored." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## A Foolish Consistency is the Hobgoblin of Litte Minds" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Code is read much more often than it is written\n", + "- Goal is to improve the readbility of code and make it consistent across the wide spectrum of Python code\n", + "- \"Readability counts\"\n", + "- A style guide is about consistency\n", + "- Consistency within a project is more important\n", + "- Consistency within a module or function is most important\n", + "- Know when to be inconsistent - Style guide reccomendations are not always applicable" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Code Layout" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Indentation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Use 4 spaces per indentation level\n", + "\n", + "Yes:\n", + "```python\n", + "# Aligned with opening delimiter.\n", + "foo = long_function_name(var_one, var_two,\n", + " var_three, var_four)\n", + "```\n", + "\n", + "Yes:\n", + "```python\n", + "# More indentation included to distinguish this from the rest.\n", + "def long_function_name(\n", + " var_one, var_two, var_three,\n", + " var_four):\n", + " print(var_one)\n", + "```\n", + "\n", + "Yes:\n", + "```python\n", + "# Hanging indents should add a level.\n", + "foo = long_function_name(\n", + " var_one, var_two,\n", + " var_three, var_four)\n", + "```\n", + "\n", + "Yes:\n", + "```python\n", + "# The closing brace/bracket/paranethesis on multiline constructs may\n", + "# line up under the first non-whitespace character\n", + "my_list = [\n", + " 1, 2, 3,\n", + " 4, 5, 6,\n", + " ]\n", + "result = some_function_that_takes_arguments(\n", + " 'a', 'b', 'c',\n", + " 'd', 'e', 'f',\n", + " )\n", + "```\n", + "\n", + "Yes:\n", + "```python\n", + "# The closing brace/bracket/paranethesis on multiline constructs may\n", + "# line up under the line that starts the mutliline construct\n", + "my_list = [\n", + " 1, 2, 3,\n", + " 4, 5, 6,\n", + "]\n", + "result = some_function_that_takes_arguments(\n", + " 'a', 'b', 'c',\n", + " 'd', 'e', 'f',\n", + ")\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "# Arguments on first line forbidden when not using vertical alignment.\n", + "foo = long_function_name(var_one, var_two,\n", + " var_three, var_four)\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "# Further indentation required as indentation is not distinguishable.\n", + "def long_function_name(\n", + " var_one, var_two, var_three,\n", + " var_four):\n", + " print(var_one)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Tabs or Spaces?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Spaces are the preferred indentation method\n", + "- Python 3 disallows mixing use of tabs and spaces\n", + "- Many modern text editors can convert tabs to spaces for you" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Maximum Line Length" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- For `jwql`, there is no maximum line length\n", + "- It is encouraged to adhere to the `PEP 8` reccomendation of 79 characters unless it reduces the readability\n", + "- 100 or more characters is on the verge of excessive" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Should a line break before or after a binary operator?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "No:\n", + "```python\n", + "# Operators sit far away from their operands\n", + "income = (gross_wages +\n", + " taxable_interest +\n", + " (dividends - qualified_dividends) -\n", + " ira_deduction -\n", + " student_loan_interest)\n", + "```\n", + "\n", + "Yes:\n", + "```python\n", + "# Easy to match operators with operands\n", + "income = (gross_wages\n", + " + taxable_interest\n", + " + (dividends - qualified_dividends)\n", + " - ira_deduction\n", + " - student_loan_interest)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Blank Lines" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Surround top-level function and class definitions with two blank lines:\n", + "\n", + "Yes:\n", + "```python\n", + "def my_first_function():\n", + " pass\n", + " \n", + "\n", + "def my_second_function():\n", + " pass\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "def my_first_function():\n", + " pass\n", + " \n", + "def my_second_function():\n", + " pass\n", + "```\n", + "\n", + "- Method definitions inside of a class are surrounded by a single blank line:\n", + "\n", + "Yes:\n", + "```python\n", + "class MyClass():\n", + "\n", + " def my_first_method():\n", + " pass\n", + " \n", + " def my_second_method():\n", + " pass\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "class MyClass():\n", + "\n", + " def my_first_method():\n", + " pass\n", + " \n", + " \n", + " def my_second_method():\n", + " pass\n", + "```\n", + "\n", + "- Extra blank lines may be used sparingly to sepeare groups of related functions/methods\n", + "- Blank lines may be ommitted between a group of related one-liners\n", + "- Use blank lines within functions and methods, sparingly, to indicate logical sections:\n", + "\n", + "Yes:\n", + "```python\n", + "def get_coordinates():\n", + " \"\"\"Read in the data file and return a list of the x and y coordinates\"\"\"\n", + " \n", + " # Read in the file\n", + " with open('my_file.txt', 'r') as f:\n", + " data = f.readlines()\n", + " \n", + " # Get the first two columns, which are the x and y coordinates\n", + " x_coords = [item.strip().split(',')[0] for item in data]\n", + " y_coords = [item.strip().split(',')[1] for item in data]\n", + " \n", + " return x_coords, y_coords\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "def get_coordinates():\n", + " \"\"\"Read in the data file and return a list of the x and y coordinates\"\"\"\n", + " # Read in the file\n", + " with open('my_file.txt', 'r') as f:\n", + " data = f.readlines()\n", + " # Get the first two columns, which are the x and y coordinates\n", + " x_coords = [item.strip().split(',')[0] for item in data]\n", + " y_coords = [item.strip().split(',')[1] for item in data]\n", + " return x_coords, y_coords\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Imports" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Imports should be on separate lines, except from importing multiple modules from one library:" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Yes:\n", + "```python\n", + "import os\n", + "import sys\n", + "```\n", + "\n", + "Yes:\n", + "```python\n", + "from subprocess import Popen, PIPE\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "import sys, os\n", + "```\n", + "\n", + "- Imports are always put at the top of the file, after module comments and docstring, but before module globals and function/class definitions:\n", + "\n", + "Yes:\n", + "```python\n", + "\"\"\"This is an example module.\n", + "\n", + "Authors\n", + "-------\n", + " Francesca Boffi\n", + "\n", + "Use\n", + "---\n", + " >>> python example.py\n", + "\"\"\"\n", + "\n", + "import os\n", + "import sys\n", + "\n", + "\n", + "def my_function():\n", + " pass\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "import os\n", + "import sys\n", + "\n", + "\"\"\"This is an example module.\n", + "\n", + "Authors\n", + "-------\n", + " Francesca Boffi\n", + "\n", + "Use\n", + "---\n", + " >>> python example.py\n", + "\"\"\"\n", + "\n", + "def my_function():\n", + " pass\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "\"\"\"This is an example module.\n", + "\n", + "Authors\n", + "-------\n", + " Francesca Boffi\n", + "\n", + "Use\n", + "---\n", + " >>> python example.py\n", + "\"\"\"\n", + "\n", + "def my_function():\n", + " pass\n", + " \n", + "import os\n", + "import sys\n", + "```\n", + "\n", + "- Imports should be grouped in the following order:\n", + " 1. Standard library imports\n", + " 2. Third pary imports\n", + " 3. local application/library specific imports\n", + "- For each group, imports should be placed in alphabetical order\n", + " \n", + "Yes:\n", + "```python\n", + "import os\n", + "import sys\n", + "\n", + "from astropy.io import fits\n", + "import numpy as np\n", + "import sqlalchemy\n", + "\n", + "from jwql.database import database_interface\n", + "from jwql.utils import utils\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "# Imports not in alphabetical order\n", + "import sys\n", + "import os\n", + "\n", + "from astropy.io import fits\n", + "import sqlalchemy\n", + "import numpy as np\n", + "\n", + "from jwql.utils import utils\n", + "from jwql.database import database_interface\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "# Imports not in groups\n", + "from astropy.io import fits\n", + "from jwql.database import database_interface\n", + "from jwql.utils import utils\n", + "import numpy as np\n", + "import os\n", + "import sqlalchemy\n", + "import sys\n", + "```\n", + "\n", + "- Always use explicit imports\n", + "\n", + "Yes:\n", + "```python\n", + "from sqlalchemy import Column, Integer, String\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "from sqlalchemy import *\n", + "```\n", + "\n", + "- Abolute imports are recommended over relative imports\n", + "\n", + "Yes:\n", + "```python\n", + "from jwql.database.database_interface import session\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "from .database.database_interface import session\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## String Quotes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Use of single or double quotes for strings is permitted\n", + "- Stay consisitent with single or double quotes within a module" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Whitespace in Expressions and Statements" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "- Avoid whitespaces immediately inside parathensis, brackets, or braces\n", + "\n", + "Yes:\n", + "```python\n", + "spam(ham[1], {eggs: 2})\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "spam( ham[ 1 ], { eggs: 2 } )\n", + "```\n", + "\n", + "- Avoid whitespaces immediately before a comma, semicolon, or colon\n", + "\n", + "Yes:\n", + "```python\n", + "if x == 4: print x, y; x, y = y, x\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "if x == 4 : print x , y ; x , y = y , x\n", + "```\n", + "\n", + "- Avoid whitespaces immediately before the open paraenthesis that starts the argument list of a function call\n", + "\n", + "Yes:\n", + "```python\n", + "spam(1)\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "spam (1)\n", + "```\n", + "\n", + "- Avoid whitespaces immediately before the open bracket that starts an indexing or slicing\n", + "\n", + "Yes:\n", + "```python\n", + "dct['key'] = lst[index]\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "dct ['key'] = lst [index]\n", + "```\n", + "\n", + "- Avoid more than one whitespace around an assignment operator in order to align it with another\n", + "\n", + "Yes:\n", + "```python\n", + "x = 1\n", + "y = 2\n", + "long_variable = 3\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "x = 1\n", + "y = 2\n", + "long_variable = 3\n", + "```\n", + "\n", + "- Avoid trailing whitespaces anywhere. Some editors have features to automatically remove these.\n", + "- Always surround the following binary operators with a single space on either side: `=`, `+=`, `-=`, `==`, `<`, `<=`, `>`, `>=`, `in`, `not`, `is`, `and`, `or`\n", + "\n", + "Yes:\n", + "```python\n", + "i = i + 1\n", + "submitted += 1\n", + "x = x*2 - 1\n", + "hypot2 = x*x + y*y\n", + "c = (a+b) * (a-b)\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "i=i+1\n", + "submitted +=1\n", + "x = x * 2 - 1\n", + "hypot2 = x * x + y * y\n", + "c = (a + b) * (a - b)\n", + "```\n", + "\n", + "- Don't use spaces around the `=` sign when used to indicate a keyword argument\n", + "\n", + "Yes:\n", + "```python\n", + "def complex(real, imag=0.0):\n", + " return magic(r=real, i=imag)\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "def complex(real, imag = 0.0):\n", + " return magic(r = real, i = imag)\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "## Comments" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "- Comments that contradict the code are worse than no comments at all\n", + "- Make it a priority to keep comments up to date when the code changes\n", + "- Comments should be complete sentences, the first word should be capitalized\n", + "- Use two spaces after a sentence-ending period in multi-sentence comments\n", + "- Each comment should start with a `#` and a single space.\n", + "- Inline comments should be separeted by at least two spaces from the end of the statement\n", + "- Inline comments are unnecessary and distracting if they state the obvious\n", + "\n", + "No:\n", + "```python\n", + "x = x + 1 # Increment x\n", + "```\n", + "\n", + "Yes:\n", + "```python\n", + "x = x + 1 # Compensate for border\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "collapsed": true + }, + "source": [ + "## Naming Conventions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Module names should follow the `lowercase_with_underscores` convention (e.g. `my_module.py`)\n", + "- Function and method names should follow the `lowercase_with_underscores` convention (e.g. `my_funciton()`)\n", + "- Class names should follow the `CamelCase` convention (e.g. `MyClass()`)\n", + "- Global variables should follow the `UPPERCASE_WITH_UNDERSCORES` convetion (e.g. `MY_GLOBAL_VAR`)\n", + "- Variable names should follow the `lowercase_with_underscores` convetnion (e.g. `my_normal_var`)\n", + "- Limit use of single character variable names to indicies and file handlers.\n", + "- Use of descriptive variable names is highly encouraged, even if it results in a long variable name\n", + "\n", + "Yes:\n", + "```python\n", + "for i, name in enumerate(names):\n", + " print(i, name)\n", + "```\n", + "\n", + "Yes:\n", + "```python\n", + "with open('filename.txt', 'r') as f:\n", + " data = f.readlines()\n", + "```\n", + "\n", + "Yes:\n", + "```python\n", + "data = np.zeros((10,10))\n", + "```\n", + "\n", + "Yes:\n", + "```python\n", + "data = np.zeros((10,10))\n", + "```\n", + "\n", + "Yes:\n", + "```python\n", + "shutter_a = 'foo'\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "d = np.zeros((10,10))\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "dat = np.zeros((10,10))\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "shutr_a = 'foo'\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Programming Reccomendations" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "- Comparison with `None` should always be done is `is` or `is not`, not with equality operators\n", + "\n", + "Yes:\n", + "```python\n", + "if foo is None:\n", + "```\n", + "\n", + "Yes:\n", + "```python\n", + "if foo is not None:\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "if not foo is None:\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "if foo != None:\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "if foo == None:\n", + "```\n", + "\n", + "- When catching exceptions, mention specific exceptions whenever possible instead of using a bare `except:` clause or catching a general `Exception`.\n", + "\n", + "Yes:\n", + "```python\n", + "try:\n", + " import platform_specific_module\n", + "except ImportError:\n", + " platform_specific_module = None\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "try:\n", + " import platform_specific_module\n", + "except Exception:\n", + " platform_specific_module = None\n", + "```\n", + "\n", + "- Limit `try` clauses to the absolute minimum amount of code necessary to avoid masking bugs\n", + "\n", + "Yes:\n", + "```python\n", + "try:\n", + " value = collection[key]\n", + "except KeyError:\n", + " return key_not_found(key)\n", + "else:\n", + " return handle_value(value)\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "try:\n", + " # Too broad!\n", + " return handle_value(collection[key])\n", + "except KeyError:\n", + " # Will also catch KeyError raised by handle_value()\n", + " return key_not_found(key)\n", + "```\n", + "\n", + "- Dont compare boolean values to `True` or `False` using `==`\n", + "\n", + "Yes:\n", + "```python\n", + "if greeting:\n", + "```\n", + "\n", + "Yes:\n", + "```python\n", + "if greeting is False:\n", + "```\n", + "\n", + "No:\n", + "```python\n", + "if greeting == True:\n", + "```" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "anaconda-cloud": {}, + "kernelspec": { + "display_name": "Python [conda env:astroconda3]", + "language": "python", + "name": "conda-env-astroconda3-py" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.4" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} From 87ebd98b8a359c88860ccd503b7f22c892b5847e Mon Sep 17 00:00:00 2001 From: Matthew Bourque Date: Tue, 2 Jan 2018 17:02:59 -0500 Subject: [PATCH 07/11] Added reccomendations section. --- style_guide.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/style_guide.md b/style_guide.md index 58fc0c95f..d8987e4d2 100644 --- a/style_guide.md +++ b/style_guide.md @@ -73,3 +73,10 @@ If `jwql` code needs to be aware of this information, it should be stored in a c - Each module should have at minimum a description, `Authors` and `Use` section. - Each function/method should have at minimum a description, `Parameters` (if necessary), and `Returns` (if necessary) sections + + +Tools and Library Recommendations +--------------------------------- + +- `argparse` for parsing command line arguments +- `bokeh` for interactive plotting From 6591f4b58c3c594417ad1d184350eb4fd534e9e4 Mon Sep 17 00:00:00 2001 From: Matthew Bourque Date: Tue, 2 Jan 2018 17:03:40 -0500 Subject: [PATCH 08/11] Moved style guide materials to a separate directory --- PEP8_examples.ipynb => style_guide/PEP8_examples.ipynb | 0 example.py => style_guide/example.py | 0 style_guide.md => style_guide/style_guide.md | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename PEP8_examples.ipynb => style_guide/PEP8_examples.ipynb (100%) rename example.py => style_guide/example.py (100%) rename style_guide.md => style_guide/style_guide.md (100%) diff --git a/PEP8_examples.ipynb b/style_guide/PEP8_examples.ipynb similarity index 100% rename from PEP8_examples.ipynb rename to style_guide/PEP8_examples.ipynb diff --git a/example.py b/style_guide/example.py similarity index 100% rename from example.py rename to style_guide/example.py diff --git a/style_guide.md b/style_guide/style_guide.md similarity index 100% rename from style_guide.md rename to style_guide/style_guide.md From 81102bbab30414632256d3a3920ca82a8e418c5f Mon Sep 17 00:00:00 2001 From: Graham Kanarek Date: Wed, 3 Jan 2018 15:10:08 -0500 Subject: [PATCH 09/11] Adding type annotations demo files I'm adding the four files I used for my demo of type annotations & mypy static type checking. --- typing_demo/typing_demo_1.py | 78 +++++++++++++++++++++++++++++++++ typing_demo/typing_demo_2.py | 83 ++++++++++++++++++++++++++++++++++++ typing_demo/typing_demo_3.py | 57 +++++++++++++++++++++++++ typing_demo/typing_demo_4.py | 17 ++++++++ 4 files changed, 235 insertions(+) create mode 100644 typing_demo/typing_demo_1.py create mode 100644 typing_demo/typing_demo_2.py create mode 100644 typing_demo/typing_demo_3.py create mode 100644 typing_demo/typing_demo_4.py diff --git a/typing_demo/typing_demo_1.py b/typing_demo/typing_demo_1.py new file mode 100644 index 000000000..8c383f73d --- /dev/null +++ b/typing_demo/typing_demo_1.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +mypy + typing demo for JWQL dev meeting 2018-1-3 + +Part 1: Intro +""" + +import sys +from typing import (List, Set, Dict, Tuple, Union, Optional, Callable, + Iterable, Any) + +assert sys.version_info >= (3, 6) # PEP 526 added variable annotations + +an_integer: int = 1 +a_float: float = 1.0 +a_bool: bool = True +a_string: str = "jwql" +a_list: List[int] = [1] +a_set: Set[int] = {1, 2, 3} +a_dict: Dict[str, bool] = {'jwql':True} # Have to specify both keys and values + +#For python versions prior to 3.6, the variable annotation syntax uses comments: +# annotated_variable = 1 # type: int + +#Tuples are a little different - we can specify a type for each element of a +#tuple because they're immutable +a_heterogeneous_tuple: Tuple[int, bool] = (1, True) +an_empty_tuple: Tuple[()] = () + +#For heterogeneous non-tuples, use Union. +a_heterogeneous_list: List[Union[int, bool, str]] = [1, True, "jwql"] +a_heterogeneous_dict: Dict[Union[str, int], Union[bool, int]] = {"jwql": True, 1:1} + +#If a value can be None, use Optional +maybe_a_string: Optional[str] = "jwql" if not a_bool else None + +#For functions, there's a similar annotation syntax +def a_generic_function(num: int) -> str: + return f"You passed {num} to this completely generic function." + +def two_arg_function(name: str, num: float = 0.0) -> None: + print(f"Sorry {name}, this function won't return {num}") + +#Function aliases and anonymous functions can also be annotated with the +#same variable syntax + +func_alias: Callable[[str, float], None] = two_arg_function +anon_func: Callable[[Any], int] = lambda x: 1 + +#Generators are just functions which return iterables: +def a_generator() -> Iterable[int]: + i = 0 + while True: + yield i + i += 1 + +#NOT RECOMMENDED +my_metavar: "hey i'm metadata!" = "not metadata" +print(__annotations__["my_metavar"]) + +#Type annotations are stored in __annotations__, either as a local variable +#or as an object attribute. + +def print_annotations(arg: Any) -> bool: + if not hasattr(arg, "__annotations__"): + print("Sorry, that argument doesn't have its own __annotations__.") + return False + print(arg.__annotations__) + return bool(arg.__annotations__) + +for name in ["an_integer", "a_generic_function", "two_arg_function", + "func_alias", "anon_func", "a_generator"]: + var = locals()[name] + print(f"Annotations for {name}:") + if not print_annotations(var): + print("Instead, we'll check the local instance of __annotations__:") + print(__annotations__[name]) diff --git a/typing_demo/typing_demo_2.py b/typing_demo/typing_demo_2.py new file mode 100644 index 000000000..90a68c74e --- /dev/null +++ b/typing_demo/typing_demo_2.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +mypy + typing demo for JWQL dev meeting 2018-1-3 + +Part 2: More advanced techniques +""" + +from typing import (Iterable, Sequence, Mapping, MutableMapping, Any, List, + Tuple, IO, ClassVar, NewType, Set, Union) +from astropy.io import fits +import numpy as np + +#Use the above generic types for standard duck typing of functions, in the +#same way that you'd use abstract base classes + +def needs_an_iterable(iterable_arg: Iterable[Any] = []) -> List[str]: + return [str(x) for x in iterable_arg] + +def dont_mutate(immut_dict: Mapping[Any, Any]) -> List[Tuple[Any, Any]]: + return list(immut_dict.items()) + +def do_mutate(mut_dict: MutableMapping[Any, Any]) -> Set[Any]: + mut_dict['jwql'] = True + return set(mut_dict.keys()) + +#Variables can be annotated without initializing +stream: IO[str] +print(__annotations__['stream']) + +#The IO type doesn't distinguish between reading, writing, or appending. +with open('demo.txt', 'w') as stream: + for i in range(10): + stream.write(f"{i}\n") + +#Pre-annotation is also useful with conditional branches +conditional: str +if "jwql": + conditional = "Yay!" +else: + conditional = "Boo!" + +#Data types from imported modules can be used just as easily as builtin types +an_array: np.ndarray = np.arange(10) +a_fits_header: fits.Header = fits.getheader("nirspec_irs2_nrs1_i_02.01.fits") + +#Class attributes and methods can be annotated as well, and user-defined +#classes can be used to annotate other variables and functions. +class aClass(object): + x: int = 0 #this is an instance variable with a default value + y: ClassVar[List[int]] #this is a class variable with no default value + + def __init__(self) -> None: #doesn't return anything + self.x = 1 + self.y = [2] + #Can also annotate attributes in __init__ + self.z: np.float64 = np.float64(3.0) + print(__annotations__) + + def result(self) -> np.float64: #self shouldn't be annotated + return x + np.array(self.y).sum() + self.z + +print(aClass.__annotations__) +an_instance: aClass = aClass() +print(__annotations__["an_instance"]) +print(an_instance.__annotations__) + +#You can use forward references if you like defining things out of order +def preemptive_function(num: "Numberlike", user: "UserID") -> None: + #Note that neither Numberlike or UserID have been defined. + print(f"Y'know, {user}...") + print(f"{num} should probably be some kind of number.") + print("Just saying...") + +#You can also define new types and type aliases +Numberlike = Union[int, float, np.float64] +UserID = NewType('UserID', str) + +#Note that you can do anything with UserID that you can do with a string, +#and can pass a UserID to any function that would accept a string. However, +#operations on UserIDs will always result in strings, not UserIDs. +output = UserID('Gray') + UserID('Kanarek') +print(output) # is of type string, not UserID. diff --git a/typing_demo/typing_demo_3.py b/typing_demo/typing_demo_3.py new file mode 100644 index 000000000..450a2c612 --- /dev/null +++ b/typing_demo/typing_demo_3.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +mypy + typing demo for JWQL dev meeting 2018-1-3 + +Part 3: mypy for type checking + +Many thanks to Tommy Tutone... +""" + +from typing import NewType + +#mypy can check for incorrectly-typed variables + +bad_variable: str = 1 #no runtime error + +#This can especially be useful when using pre-annotation, since types can be +#hinted before calculations, I/O, or other complex code determines its value. +jenny: str + +#mypy can also check function arguments and return values +def ive_got_your_number(num: int) -> bool: + if num == 867_5309: + return True + else: + return "Jenny don't change your number" + +ive_got_your_number("jenny") #no runtime error +ive_got_your_number(555_1212) #no runtime error + +if ive_got_your_number(8675_309): + jenny = 867_5309 #no runtime error +else: + jenny = "Don't change your number" + +#If for some reason you don't want a particular variable's type to be checked, +#then use comment syntax and "ignore" +dummy = None # type: ignore # otherwise this will throw a mypy error! + +#mypy can handle user-created types +UserID = NewType("UserID", str) + +gray: UserID = UserID("Gray") +kanarek: UserID = "Kanarek" #no runtime error + +user: UserID = gray + kanarek #no runtime error + +def get_first_char(user: UserID) -> str: + return user[0] + +get_first_char(gray) +get_first_char("Gray") #no runtime error + + +#mypy can help you figure out the types of variables, if it's complicated to +#find out beforehand +reveal_type(1) diff --git a/typing_demo/typing_demo_4.py b/typing_demo/typing_demo_4.py new file mode 100644 index 000000000..eaf0ef4c9 --- /dev/null +++ b/typing_demo/typing_demo_4.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +""" +mypy + typing demo for JWQL dev meeting 2018-1-3 + +Part 4: Subtlety +""" + +#Why do we care about this? Because errors can be subtle. + +#A simple example! + +def get_favorite_number(): + return input("What's your favorite number? ") + +num = get_favorite_number() +print("Twice your favorite number is", num*2) From 7b794e35cf97a7c1bc5575ee2d499e0f4c0d67cb Mon Sep 17 00:00:00 2001 From: Graham Kanarek Date: Wed, 3 Jan 2018 15:21:40 -0500 Subject: [PATCH 10/11] Adding type annotations to the example I'm not sure whether some explanatory text should be included here about the type annotations, or if it should just go in the style guide... --- style_guide/example.py | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/style_guide/example.py b/style_guide/example.py index 4003d0d2d..44158b259 100644 --- a/style_guide/example.py +++ b/style_guide/example.py @@ -59,6 +59,7 @@ import glob import os import sys +from typing import List, Union, Tuple, Optional, Any, Dict from astropy.io import fits import matplotlib.pyplot as plt @@ -69,10 +70,10 @@ # Global variables should be avioded, but if used should be named with # all-caps -A_GLOBAL_VARIABLE = 'foo' +A_GLOBAL_VARIABLE = 'foo' # type: str -def my_main_function(path, filter): +def my_main_function(path: str, filter: str) -> None: """The main function of the ``example`` module. This function performs the main tasks of the module. See module @@ -87,20 +88,20 @@ def my_main_function(path, filter): """ print('Using {} as an input file'.format(path)) - an_int = 1 - a_float = 3.14 - a_bool = True - a_list = ['Dog', 'Cat', 'Turtle', False, 7] - a_tuple = ('Dog', 'Cat', 'Turtle', False, 7) - a_dict = {'key1': 'value1', 'key2': 'value2'} - an_obj = object() + an_int = 1 # type: int + a_float = 3.14 # type: float + a_bool = True # type: bool + a_list = ['Dog', 'Cat', 'Turtle', False, 7] # type: List[Union[str, bool, int]] + a_tuple = ('Dog', 'Cat', 'Turtle', False, 7) # type: Tuple[str, str, str, bool, int] + a_dict = {'key1': 'value1', 'key2': 'value2'} # type: Dict[str, str] + an_obj = object() # type: object - result = some_other_function(an_int, a_float, a_bool, a_list, a_tuple, a_dict, an_obj) + result = some_other_function(an_int, a_float, a_bool, a_list, a_tuple, a_dict, an_obj) # type: Optional[int] print(result) -def parse_args(): +def parse_args() -> argparse.Namespace: """Parse command line arguments. Returns ``args`` object. Returns @@ -110,11 +111,11 @@ def parse_args(): """ # Create help strings - path_help = 'The path to the input file.' - filter_help = 'The filter to process (e.g. "F606W").' + path_help = 'The path to the input file.' # type: str + filter_help = 'The filter to process (e.g. "F606W").' # type: str # Add arguments - parser = argparse.ArgumentParser() + parser = argparse.ArgumentParser() # type: argparse.ArgumentParser parser.add_argument('path', type=str, default=os.getcwd(), @@ -127,12 +128,13 @@ def parse_args(): help=filter_help) # Parse args - args = parser.parse_args() + args = parser.parse_args() # type: argparse.Namespace return args -def some_other_function(an_int, a_float, a_bool, a_list, a_tuple, a_dict, an_obj): +def some_other_function(an_int: int, a_float: float, a_bool: bool, a_list: List[Any], + a_tuple: Tuple[Any], a_dict: Dict[Any, Any], an_obj: object) -> int: """This function just does a bunch of nonsense. But it serves as a decent example of some things. @@ -166,12 +168,13 @@ def some_other_function(an_int, a_float, a_bool, a_list, a_tuple, a_dict, an_obj # Operators should be separated by spaces print(a_float + a_float) - + + return an_int if __name__ == '__main__': - args = parse_args() + args = parse_args() # type: argparse.Namespace my_main_function(args.path, args.filter) From 50dde4100bb05c86a31a1f75c46f7443d9bd6073 Mon Sep 17 00:00:00 2001 From: Graham Kanarek Date: Tue, 16 Jan 2018 15:29:34 -0500 Subject: [PATCH 11/11] Include type annotations note in style_guide.md I've added a bullet point to the section for `jwql`-specific standards to recommend using type annotations & `mypy`. --- style_guide/style_guide.md | 1 + 1 file changed, 1 insertion(+) diff --git a/style_guide/style_guide.md b/style_guide/style_guide.md index d8987e4d2..4280d4063 100644 --- a/style_guide/style_guide.md +++ b/style_guide/style_guide.md @@ -64,6 +64,7 @@ If `jwql` code needs to be aware of this information, it should be stored in a c Additionally, the code shall adhere to the following special guidelines: - Function and class definitions should be placed in alphabetical order in the module + - It is encouraged to annotate variables and functions using the [`typing`](https://docs.python.org/3/library/typing.html) module (see [PEP 483](https://www.python.org/dev/peps/pep-0483/), [PEP 484](https://www.python.org/dev/peps/pep-0484/), and [PEP 526](https://www.python.org/dev/peps/pep-0526/)). In addition, it is recommended that code be type-checked using [`mypy`](http://mypy-lang.org/) before a pull request is submitted. `jwql`-Specific Documentation Standards