Skip to content

Commit

Permalink
Merge pull request #98 from openpassword/develop
Browse files Browse the repository at this point in the history
Release?
  • Loading branch information
nnevala committed Jan 27, 2015
2 parents e29fe5c + 6b98f6d commit b3532f3
Show file tree
Hide file tree
Showing 72 changed files with 6,247 additions and 8 deletions.
5 changes: 5 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[run]
branch = True

[report]
show_missing = True
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
*.pyc
__pycache__/
.coverage
.coverage.*
167 changes: 167 additions & 0 deletions .scrutinizer.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
checks:
python:
code_rating: true
duplicate_code: true
variables_unused_wildcard_import: true
variables_used_before_assignment: true
variables_unused_variable: true
variables_unused_import: true
variables_unused_argument: true
variables_unpacking_non_sequence: true
variables_undefined_variable: true
variables_undefined_loop_variable: true
variables_undefined_all_variable: true
variables_unbalanced_tuple_unpacking: true
variables_redefined_outer_name: true
variables_redefined_builtin: true
variables_redefine_in_handler: true
variables_no_name_in_module: true
variables_invalid_all_object: true
variables_global_variable_undefined: true
variables_global_variable_not_assigned: true
variables_global_statement: true
variables_global_at_module_level: true
typecheck_unexpected_keyword_arg: true
typecheck_too_many_function_args: true
typecheck_redundant_keyword_arg: true
typecheck_not_callable: true
typecheck_no_value_for_parameter: true
typecheck_no_member: true
typecheck_missing_kwoa: true
typecheck_maybe_no_member: true
typecheck_duplicate_keyword_arg: true
typecheck_assignment_from_none: true
typecheck_assignment_from_no_return: true
string_unused_format_string_key: true
string_truncated_format_string: true
string_too_many_format_args: true
string_too_few_format_args: true
string_mixed_format_string: true
string_missing_format_string_key: true
string_format_needs_mapping: true
string_constant_anomalous_unicode_escape_in_string: true
string_constant_anomalous_backslash_in_string: true
string_bad_str_strip_call: true
string_bad_format_string_key: true
string_bad_format_character: true
open_mode_bad_open_mode: true
newstyle_bad_super_call: true
miscellaneous_fixme: true
logging_unsupported_format: true
logging_too_many_args: true
logging_too_few_args: true
logging_not_lazy: true
logging_format_truncated: true
imports_wildcard_import: true
imports_relative_import: true
imports_reimported: true
imports_import_self: true
imports_import_error: true
imports_deprecated_module: true
imports_cyclic_import: true
format_unnecessary_semicolon: true
format_trailing_whitespace: true
format_superfluous_parens: true
format_old_ne_operator: true
format_multiple_statements: true
format_mixed_indentation: true
format_missing_final_newline: true
format_lowercase_l_suffix: true
format_line_too_long:
max_length: '120'
format_bad_whitespace: true
format_bad_indentation:
indentation: '4 spaces'
format_backtick: true
exceptions_raising_string: true
exceptions_raising_non_exception: true
exceptions_raising_bad_type: true
exceptions_pointless_except: true
exceptions_notimplemented_raised: true
exceptions_catching_non_exception: true
exceptions_broad_except: true
exceptions_binary_op_exception: true
exceptions_bare_except: true
exceptions_bad_except_order: true
design_interface_not_implemented: true
design_abstract_class_not_used: true
classes_valid_slots: true
classes_super_init_not_called: true
classes_signature_differs: true
classes_protected_access: true
classes_non_parent_init_called: true
classes_non_iterator_returned: true
classes_no_self_argument: true
classes_no_method_argument: true
classes_missing_interface_method: true
classes_method_hidden: true
classes_interface_is_not_class: true
classes_bad_staticmethod_argument: true
classes_bad_mcs_method_argument: true
classes_bad_mcs_classmethod_argument: true
classes_bad_context_manager: true
classes_bad_classmethod_argument: true
classes_attribute_defined_outside_init: true
classes_arguments_differ: true
classes_access_member_before_definition: true
classes_abstract_method: true
basic_yield_outside_function: true
basic_useless_else_on_loop: true
basic_unreachable: true
basic_unnecessary_pass: true
basic_unnecessary_lambda: true
basic_star_args: true
basic_return_outside_function: true
basic_return_in_init: true
basic_return_arg_in_generator: true
basic_pointless_string_statement: true
basic_pointless_statement: true
basic_old_raise_syntax: true
basic_not_in_loop: true
basic_nonexistent_operator: true
basic_missing_reversed_argument: true
basic_missing_module_attribute: true
basic_lost_exception: true
basic_invalid_name:
functions: '[a-z_][a-z0-9_]{2,80}$'
variables: '[a-z_][a-z0-9_]{2,80}$'
whitelisted_names: 'i,j,k,ex,Run,_'
constants: '(([A-Z_][A-Z0-9_]*)|(__.*__))$'
attributes: '[a-z_][a-z0-9_]{2,30}$'
arguments: '[a-z_][a-z0-9_]{2,30}$'
class_attributes: '([A-Za-z_][A-Za-z0-9_]{2,80}|(__.*__))$'
inline_vars: '[A-Za-z_][A-Za-z0-9_]*$'
classes: '[A-Z_][a-zA-Z0-9]+$'
modules: '(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$'
methods: '[a-z_][a-z0-9_]{2,80}$'
basic_init_is_generator: true
basic_function_redefined: true
basic_expression_not_assigned: true
basic_exec_used: true
basic_eval_used: true
basic_duplicate_key: true
basic_duplicate_argument_name: true
basic_dangerous_default_value: true
basic_assert_on_tuple: true
basic_abstract_class_instantiated: true
basic_bad_reversed_sequence: true

filter:
excluded_paths:
- spec/*
- tests/*
- features/*

build:
environment:
python: 3.4.1
dependencies:
override:
- "pip3 install -r requirements.txt"

tools:
external_code_coverage: true

before_commands:
- "pip3 install -r requirements.txt"

11 changes: 10 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
language: python
python:
- "3.3"
- "3.4"
# command to run tests
script: nosetests
script: sh bin/tests

notifications:
irc:
channels:
- "chat.freenode.net#openpassword"
on_success: always
on_failure: always
skip_join: true
167 changes: 167 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# CONTRIBUTING

## The Development Philosophy

Blimey is developed under the MIT license, which means it's Open Source and that we're accepting contributions to the project.

If you're thinking about contributing to blimey, this page is the place to start.

We try to build blimey as a robust piece of software, with a simple and elegant design. To try and achieve this we apply the principles of emergent design, and expect contributions to do so as well.

Quoting "The RSpec Book":

> "BDD calls Test-Driven Development coding by example, which places emphasis on using examples to drive out the behaviour of the code. The fact that these examples become tests once the code is written is a secondary concern"
So, what does this mean in the context of blimey?
It means that we couldn't care less about "code coverage" or writing tests for the sake of doing it.
It means that we write examples of how we imagine our code will be used, before writing our code, and then we write the minimum amount of code to make the expectations of those examples to pass.
It means that we don't do a lot of upfront design, instead, we let it emerge as a response to our examples.

The consequences of this are often: small, simple and clean methods and classes. Responsibility separation in methods and classes. Injection of dependencies. And a number of other advantages that become clear as the software progresses.

## Tools

Blimey is developed in **Python 3**. We try to keep the number of dependencies low, so currently we're using the following tools to help us achieve our goals:

* nose - A testing framework for Python
* spec - A nose plugin to allow for a more BBD like output
* fudge - A mocking framework for Python
* tissue - A nose plugin to check code for PEP8 style violations
* pbkdf2 - A python library for implementing the [Password-Based Key Derivation Function 2](http://en.wikipedia.org/wiki/PBKDF2)
* pycrypto - A python cryptography library

To install node you'll need to run pip. Depending on your setup, you pip executable might be called pip or pip3:

`pip3 install nose`

For the remaining tools, you can take advantage of the requirements.txt contained in the repository:

`pip3 install -r requirements.txt --use-mirrors`

## Running the examples

To run the examples you'll just need to run the script provided in bin/tests. From the root of the project execute the following:

`bin/tests`

This will run nosetests and pep8 with the appropriate parameters.

## Writing a feature

Here's a guide for how to develop a feature following the development philosophy described above.
Let's imagine we're writing a class called *LightBulb* inside the *blimey* package:

- Create a file called `spec/blimey/light_bulb_spec.py`
- Import the (still inexistent) class you're going to write:

```python
from blimey.light_bulb import LightBulb
```

- Inside that file, create a class called *LightBulbSpec*
- Create the first example, describing one behaviour of your class, start with the word "it". Let's say *it_should_be_lit_after_being_turned_on* which shoud look something like this:

```python
from nose.tools import *
from blimey.light_bulb import LightBulb


class LightBulbSpec:
def it_should_be_lit_after_being_turned_on(self):
bulb = LightBulb()
bulb.turn_on()

eq_(bulb.turned_on(), True)

```
- Run `bin/tests`
- Fix any code styling issues that it may report
- At this point you should get the following error `ImportError: No module named 'blimey.light_bulb'`. Fix it by creating the file `blimey/light_bulb.py`.
- Run `bin/tests` again. You should now get the error `ImportError: cannot import name LightBulb`.
- Create the *LightBulb* class inside the `blimey.light_bulb`. At this point your class should have nothing else other then a *pass* statement.

```python
class LightBulb:
pass

```
- Run `bin/tests` again. The error should now be `AttributeError: 'LightBulb' object has no attribute 'turn_on'`. So let's create it.

```python
class LightBulb:

def turn_on(self):
pass

```
- Once more run `bin/tests`. You should be getting the hang of this now, we're letting our example lead what we do. It's important not to write more than what our example is leading us to do, otherwise our design won't emerge from our idealised example. Now you should get this error `AttributeError: 'LightBulb' object has no attribute 'turned_on'`, so let's write our turned_on method.

```python
class LightBulb:

def turn_on(self):
pass

def turned_on(self):
return True

```
- Run `bin/tests` and all our tests should now pass. But..., our LightBulb class doesn't seem all that useful. turned_on always return *True*. What's up with that? Well, that's all we need it to do for now, but our light bulb does more that just stay lit. So, let's describe another behaviour. Let's say, `it_should_not_lit_before_being_turned_on`.

```python
from nose.tools import *
from blimey.light_bulb import LightBulb


class LightBulbSpec:
def it_should_be_lit_after_being_turned_on(self):
bulb = LightBulb()
bulb.turn_on()

eq_(bulb.turned_on(), True)

def it_should_not_lit_before_being_turned_on(self):
bulb = LightBulb()
eq_(bulb.turned_on(), False)

```
- Now our `it should not lit before being turned on` test is failling with the following error `AssertionError: True != False`. Ok, so our example is telling us to change our behaviour, so let's do it. Although we could do this in one step, for clarity sake let's do it in two:

```python
class LightBulb:
def __init__(self):
self._on = False

def turn_on(self):
pass

def turned_on(self):
return self._on

```
- We fixed our test, but we broke `it should be lit after being turned on`. This is our example driving us to change our incorrect implementation. In response to this we fix the turn_on method:

```python
class LightBulb:
def __init__(self):
self._on = False

def turn_on(self):
self._on = True

def turned_on(self):
return self._on

```

This should drive our implementation to be simple and make our interface a result of what we wish we had, producing better and clear code.
In this example refactoring is not needed due to it's extreme simplicity, but **every time you get green, stop, look at both your code and your tests, and search for possible ways of improving it without changing or adding functionality**. Rename methods and properties which have names that are not explicit enough, remove complexity, generally clean your code. Do only one change at a time and alway run the tests after making the change. This step is vital to achieve the goals to which this methodology aspires.

## Making pull requests

Before making a pull request, be sure to run nose and pep8, using the bin/tests script, to ensure that your code is ready to be merged.
Both specs and code should follow the pep8 standards.

To make this easier, we provide a git_hooks folder that contains a pre-commit hook, that will do the work for you. Just copy it to your repository git hooks folder. From the base folder of your repository do:

`cp ./git_hooks/pre_commit .git/hooks`
Loading

0 comments on commit b3532f3

Please sign in to comment.