From 9ec91cc8474353e2e9dc39d134c616924319caa9 Mon Sep 17 00:00:00 2001 From: Nils Diefenbach <23okrs20+github@mykolab.com> Date: Thu, 23 Nov 2017 10:30:33 +0100 Subject: [PATCH] Add linters to ci (#29) * added linters to travis * Revert "added linters to travis" This reverts commit 257b79f13fbf4cb38896d61650f3f82da387d627. * added pylint and pydocstyle to CI yml * added default pylint config * PEP328: use absolute imports * added missing DOCstrings * PEP8 attribute names must be lower case and separated by underscore * PEP8 attribute names must be lower case and separated by underscore * Revert "PEP8 attribute names must be lower case and separated by underscore" This reverts commit 2d01bb4bd28910bd087377ccbaf46b87fb4c7cf9. * Revert "Revert "PEP8 attribute names must be lower case and separated by underscore"" This reverts commit e96ef7e3b893dd1f11a2ad71f6070fe6e8780ded. revert revert! What a hoot. * PEP8 Attr names must be lower case * removed unnecessary import of object * added docstrings, converted method to static * PEP238 - use absolute import; module docstring added * added pep8 spacing * PEP8: spacing, added missing docstrings * Added docstrings, removes builtins import * Added missing docstring * reverted change to deserialize * pydocstyle linter fixes * added staticmethod back to validate_sort_input * added alt-import for mock if unavailable * removed staticmethod decorator; see comment --- .travis.yml | 3 + pirant/__init__.py | 3 +- pirant/app.py | 75 ++++++-- pirant/handlers.py | 55 ++++-- pirant/models.py | 38 ++++ pirant/urlbuilder.py | 55 +++--- pirant/utils.py | 11 +- pylint.rc | 425 +++++++++++++++++++++++++++++++++++++++++ tests/test_app.py | 7 +- tests/test_handlers.py | 7 +- 10 files changed, 615 insertions(+), 64 deletions(-) create mode 100644 pylint.rc diff --git a/.travis.yml b/.travis.yml index e553b74..d9301da 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,11 +10,14 @@ python: - "pypy-5.3.1" install: + - pip install pylint pydocstyle - pip install -U setuptools - pip install -e . - pip install -r requirements-dev.txt script: + - pylint --rcfile=pylint.rc pirant + - pydocstyle pirant - pytest tests - coverage run --source=pirant setup.py test diff --git a/pirant/__init__.py b/pirant/__init__.py index 2ddfbef..d012dcb 100644 --- a/pirant/__init__.py +++ b/pirant/__init__.py @@ -1,2 +1,3 @@ +"""Python-based API client for DevRant.""" from __future__ import absolute_import, division, print_function -from .app import DevRant +from pirant.app import DevRant diff --git a/pirant/app.py b/pirant/app.py index bba43bb..60972fa 100644 --- a/pirant/app.py +++ b/pirant/app.py @@ -1,34 +1,77 @@ +"""DevRant class providing API methods.""" + from __future__ import absolute_import, division, print_function -from builtins import object -from .handlers import RequestHandler, ResponseHandler +from pirant.handlers import RequestHandler, ResponseHandler class DevRant(object): + """API Class providing interface methods.""" def __init__(self): - self.RequestHandler = RequestHandler() - self.ResponseHandler = ResponseHandler() + """Initialize the class instance.""" + self.request_handler = RequestHandler() + self.response_handler = ResponseHandler() def get_rants(self, sort, limit, skip): - response = self.RequestHandler.get_rants(sort, limit, skip) - return self.ResponseHandler.get_rants_build_response(response) + """ + Fetch rants from API. + + :param sort: + :param limit: + :param skip: + :return: + """ + response = self.request_handler.get_rants(sort, limit, skip) + return self.response_handler.get_rants_build_response(response) def get_rant_by_id(self, rant_id): - response = self.RequestHandler.get_rant_by_id(rant_id) - return self.ResponseHandler.get_rant_by_id_build_response(response) + """ + Feth a rant by its rant ID. + + :param rant_id: + :return: + """ + response = self.request_handler.get_rant_by_id(rant_id) + return self.response_handler.get_rant_by_id_build_response(response) def get_weekly_rants(self, sort, skip): - response = self.RequestHandler.get_weekly_rants(sort, skip) - return self.ResponseHandler.get_rants_build_response(response) + """ + Fetch the weekly rants. + + :param sort: + :param skip: + :return: + """ + response = self.request_handler.get_weekly_rants(sort, skip) + return self.response_handler.get_rants_build_response(response) def search_rants_by_keyword(self, keyword): - response = self.RequestHandler.search_rants_by_keyword(keyword) - return self.ResponseHandler.search_rants_by_keyword_build_response(response) + """ + Search rants by given keyword. + + :param keyword: + :return: + """ + response = self.request_handler.search_rants_by_keyword(keyword) + return self.response_handler.search_rants_by_keyword_build_response(response) def get_collabs(self, skip, limit): - response = self.RequestHandler.get_collabs(skip, limit) - return self.ResponseHandler.get_collabs_build_response(response) + """ + Fetch collabs from the API. + + :param skip: + :param limit: + :return: + """ + response = self.request_handler.get_collabs(skip, limit) + return self.response_handler.get_collabs_build_response(response) def get_collab_by_id(self, collab_id): - response = self.RequestHandler.get_rant_by_id(collab_id) - return self.ResponseHandler.get_rant_by_id_build_response(response) + """ + Fetch a collab by its collab ID. + + :param collab_id: + :return: + """ + response = self.request_handler.get_rant_by_id(collab_id) + return self.response_handler.get_rant_by_id_build_response(response) diff --git a/pirant/handlers.py b/pirant/handlers.py index 5105653..f0b8383 100644 --- a/pirant/handlers.py +++ b/pirant/handlers.py @@ -1,61 +1,78 @@ +"""Request and Response handlers for interfacing with the DevRant API.""" from __future__ import absolute_import, division, print_function import json import requests -from builtins import object -from .models import RantsResponse, RantResponse, SearchResponse -from .urlbuilder import URLBuilder +from pirant.models import RantsResponse, RantResponse, SearchResponse +from pirant.urlbuilder import URLBuilder class ResponseHandler(object): + """Handles deserialization of API responses to pirant Response Objects.""" def __init__(self): - self.RantsResponse = RantsResponse() - self.RantResponse = RantResponse() - self.SearchResponse = SearchResponse() + """Initialize the instance.""" + self.rants_response = RantsResponse() + self.rant_response = RantResponse() + self.search_response = SearchResponse() - def build_response(self, model, response): + @staticmethod + def build_response(model, response): + """Build a response for the given Response Object and model.""" json_string = json.loads(response.content) deserialized = model.deserialize(json_string) return deserialized - + def get_rants_build_response(self, response): - return self.build_response(self.RantsResponse, response) + """Deserialize the given Rants to RantsResponse object.""" + return self.build_response(self.rants_response, response) def get_rant_by_id_build_response(self, response): - return self.build_response(self.RantResponse, response) + """Deserialize the given Rant to RantsResponse object.""" + return self.build_response(self.rant_response, response) + # pylint: disable=invalid-name def search_rants_by_keyword_build_response(self, response): - return self.build_response(self.SearchResponse, response) + """Deserialize the given Search to SerachResponse object.""" + return self.build_response(self.search_response, response) def get_collabs_build_response(self, response): - return self.build_response(self.RantsResponse, response) + """Deserialize the given Collabs to RantsResponse object.""" + return self.build_response(self.rants_response, response) + class RequestHandler(object): + """Handles generating a request URL for pirant Requests.""" def __init__(self): - self.UrlBuilder = URLBuilder() + """Initialize the instance.""" + self.url_builder = URLBuilder() def get_rants(self, sort, limit, skip): - url = self.UrlBuilder.get_rants_url(sort, limit, skip) + """Build a request to get rants, send it and return its repsonse.""" + url = self.url_builder.get_rants_url(sort, limit, skip) response = requests.get(url) return response def get_rant_by_id(self, rant_id): - url = self.UrlBuilder.get_rant_by_id_url(rant_id) + """Build the request to get a rant by id, send it and return its repsonse.""" + url = self.url_builder.get_rant_by_id_url(rant_id) response = requests.get(url) return response def get_weekly_rants(self, sort, skip): - url = self.UrlBuilder.get_weekly_rant_url(sort, skip) + """Build the request to get the weekly rants, send it and return its repsonse.""" + url = self.url_builder.get_weekly_rant_url(sort, skip) response = requests.get(url) return response def search_rants_by_keyword(self, keyword): - url = self.UrlBuilder.search_rants_by_keywords(keyword) + """Build the request to search for rants by keyword, send it and return its repsonse.""" + url = self.url_builder.search_rants_by_keywords(keyword) response = requests.get(url) return response - + def get_collabs(self, skip, sort): - url = self.UrlBuilder.get_collabs_url(skip, sort) + """Build the request to get all collabs, send it and return its repsonse.""" + url = self.url_builder.get_collabs_url(skip, sort) response = requests.get(url) return response diff --git a/pirant/models.py b/pirant/models.py index df79c0c..e217f5e 100644 --- a/pirant/models.py +++ b/pirant/models.py @@ -1,7 +1,14 @@ +"""Supplies colander Models.""" + +# pylint: disable=too-few-public-methods,invalid-name,missing-docstring + from __future__ import absolute_import, division, print_function import colander + class News(colander.MappingSchema): + """:class:`colander.MappingScheme` for News articles.""" + id = colander.SchemaNode(colander.Int(), missing=colander.drop) type = colander.SchemaNode(colander.String(), missing=colander.drop) headline = colander.SchemaNode(colander.String(), missing=colander.drop) @@ -10,24 +17,38 @@ class News(colander.MappingSchema): height = colander.SchemaNode(colander.Int(), missing=colander.drop) action = colander.SchemaNode(colander.String(), missing=colander.drop) + class Tags(colander.SequenceSchema): + """:class:`colander.SequenceSchema` for tags.""" + tag = colander.SchemaNode(colander.String()) + class Image(colander.MappingSchema): + """:class:`colander.MappingScheme` for Images.""" + url = colander.SchemaNode(colander.String()) width = colander.SchemaNode(colander.Int()) height = colander.SchemaNode(colander.Int()) + # pylint: disable=signature-differs def deserialize(self, cstruct): + """Deserialize the given CStruct for further use by pirant.""" if cstruct == "": cstruct = {} return cstruct + class UserAvatar(colander.MappingSchema): + """:class:`colander.MappingScheme` for user Avatars.""" + b = colander.SchemaNode(colander.String(), missing=colander.drop) i = colander.SchemaNode(colander.String(), missing=colander.drop) + class Rant(colander.MappingSchema): + """:class:`colander.MappingScheme` for Rants.""" + id = colander.SchemaNode(colander.Int()) text = colander.SchemaNode(colander.String()) score = colander.SchemaNode(colander.Int()) @@ -54,9 +75,14 @@ class Rant(colander.MappingSchema): class Rants(colander.SequenceSchema): + """:class:`colander.SequenceSchema` for Images.""" + rant = Rant() + class RantsResponse(colander.MappingSchema): + """:class:`colander.MappingSchema` for RantsResponse objects.""" + rants = Rants() news = News(missing=colander.drop) success = colander.SchemaNode(colander.Boolean(), missing=colander.drop) @@ -65,7 +91,10 @@ class RantsResponse(colander.MappingSchema): set = colander.SchemaNode(colander.String(), missing=colander.drop) wrw = colander.SchemaNode(colander.Int(), missing=colander.drop) + class Comment(colander.MappingSchema): + """:class:`colander.MappingSchema` for comments.""" + id = colander.SchemaNode(colander.Int()) rant_id = colander.SchemaNode(colander.Int()) body = colander.SchemaNode(colander.String()) @@ -77,16 +106,25 @@ class Comment(colander.MappingSchema): user_username = colander.SchemaNode(colander.String()) user_userscore = colander.SchemaNode(colander.Int(), missing=colander.drop) + class Comments(colander.SequenceSchema): + """:class:`colander.SequenceSchema` for comment Mappings.""" + comment = Comment() + class RantResponse(colander.MappingSchema): + """:class:`colander.MappingSchema` for RantResponse objects.""" + comments = Comments() rant = Rant() success = colander.SchemaNode(colander.Boolean(), missing=colander.drop) error = colander.SchemaNode(colander.Boolean(), missing=colander.drop) + class SearchResponse(colander.MappingSchema): + """:class:`colander.MappingSchema` for SearchResponse objects.""" + success = colander.SchemaNode(colander.Boolean(), missing=colander.drop) error = colander.SchemaNode(colander.Boolean(), missing=colander.drop) results = Rants() diff --git a/pirant/urlbuilder.py b/pirant/urlbuilder.py index 3009a30..30b3973 100644 --- a/pirant/urlbuilder.py +++ b/pirant/urlbuilder.py @@ -1,53 +1,64 @@ +"""URL constructor class to generate valid API request URLs.""" from __future__ import absolute_import, division, print_function -from builtins import str -from builtins import object + class URLBuilder(object): + """Handles generating valid URLs to requests resources from DevRant.""" + + # pylint: disable=too-many-instance-attributes,too-many-function-args + def __init__(self): - self.APP_VERSION = 3 - self.BASE_URL = "https://www.devrant.com/api/devrant" - self.RANTS_URL = "%s/rants?sort=%s&limit=%d&skip=%d&app=%d" - self.WEEKLY_RANTS_URL = "%s/weekly-rants?sort=%s&skip=%d&app=%d" - self.RANT_URL = "%s/rants/%d?app=%d" - self.SEARCH_URL = "%s/search?term=%s&app=%d" - self.COLLABS_URL = "%s/collabs?app=%d&skip=%d&limit=%d" - self.SORT_TOP = "top" - self.SORT_ALGO = "algo" - self.SORT_RECENT = "recent" - self.VALID_SORTS = [self.SORT_ALGO, self.SORT_RECENT, self.SORT_TOP] + """Initialize class instance.""" + self.app_version = 3 + self.base_url = "https://www.devrant.com/api/devrant" + self.rants_url = "%s/rants?sort=%s&limit=%d&skip=%d&app=%d" + self.weekly_rants_url = "%s/weekly-rants?sort=%s&skip=%d&app=%d" + self.rant_url = "%s/rants/%d?app=%d" + self.search_url = "%s/search?term=%s&app=%d" + self.collabs_url = "%s/collabs?app=%d&skip=%d&limit=%d" + self.sort_top = "top" + self.sort_algo = "algo" + self.sort_recent = "recent" + self.valid_sorts = [self.sort_algo, self.sort_recent, self.sort_top] def get_rants_url(self, sort, limit, skip): - sort = self.validate_sort_input(self, sort) + """Generate a request URL to get available rants.""" + sort = self.validate_sort_input(sort) limit = self.validate_int_input(limit) skip = self.validate_int_input(skip) - return str(self.RANTS_URL % (self.BASE_URL, sort, limit, skip, self.APP_VERSION)) + return str(self.rants_url % (self.base_url, sort, limit, skip, self.app_version)) def get_rant_by_id_url(self, rant_id): - return str(self.RANT_URL % (self.BASE_URL, rant_id, self.APP_VERSION)) + """Generate a request URL to get a rant by its id.""" + return str(self.rant_url % (self.base_url, rant_id, self.app_version)) def get_weekly_rant_url(self, sort, skip): - sort = self.validate_sort_input(self, sort) + """Generate a request URL to get the weekly rants.""" + sort = self.validate_sort_input(sort) skip = self.validate_int_input(skip) - return str(self.WEEKLY_RANTS_URL % (self.BASE_URL, sort, skip, self.APP_VERSION)) + return str(self.weekly_rants_url % (self.base_url, sort, skip, self.app_version)) def search_rants_by_keywords(self, keyword): - return str(self.SEARCH_URL % (self.BASE_URL, keyword, self.APP_VERSION)) + """Generate a request URL to search rants by keywords.""" + return str(self.search_url % (self.base_url, keyword, self.app_version)) def get_collabs_url(self, skip, limit): + """Generate a request URL to get available collabs.""" limit = self.validate_int_input(limit) skip = self.validate_int_input(skip) - return str(self.COLLABS_URL % (self.BASE_URL, self.APP_VERSION, skip, limit)) + return str(self.collabs_url % (self.base_url, self.app_version, skip, limit)) - @staticmethod def validate_sort_input(self, sort_type): + """Validate that input for sort has proper type.""" if sort_type == "": sort_type = "top" - elif sort_type not in self.VALID_SORTS: + elif sort_type not in self.valid_sorts: raise ValueError("Invalid Sort type") return sort_type @staticmethod def validate_int_input(some_int): + """Validate that integer is not negative.""" if some_int >= 0: return some_int raise ValueError("Positive integer required") diff --git a/pirant/utils.py b/pirant/utils.py index 74f7af2..f0a2574 100644 --- a/pirant/utils.py +++ b/pirant/utils.py @@ -1,11 +1,14 @@ +"""Utility classes for testing.""" from __future__ import absolute_import, division, print_function -from builtins import object + +# pylint: disable=too-few-public-methods,fixme + class MockHttpResponse(object): - """ - Test Util: mocks response - """ + """Test Util: mocks response.""" + # TODO Move this to helpers under tests directory def __init__(self, content, status_code): + """Initialize mock instance.""" self.content = content self.status_code = status_code diff --git a/pylint.rc b/pylint.rc new file mode 100644 index 0000000..019ae03 --- /dev/null +++ b/pylint.rc @@ -0,0 +1,425 @@ +[MASTER] + +# A comma-separated list of package or module names from where C extensions may +# be loaded. Extensions are loading into the active Python interpreter and may +# run arbitrary code +extension-pkg-whitelist= + +# Add files or directories to the blacklist. They should be base names, not +# paths. +ignore=CVS + +# Add files or directories matching the regex patterns to the blacklist. The +# regex matches against base names, not paths. +ignore-patterns= + +# Python code to execute, usually for sys.path manipulation such as +# pygtk.require(). +#init-hook= + +# Use multiple processes to speed up Pylint. +jobs=1 + +# List of plugins (as comma separated values of python modules names) to load, +# usually to register additional checkers. +load-plugins= + +# Pickle collected data for later comparisons. +persistent=yes + +# Specify a configuration file. +#rcfile= + +# Allow loading of arbitrary C extensions. Extensions are imported into the +# active Python interpreter and may run arbitrary code. +unsafe-load-any-extension=no + + +[MESSAGES CONTROL] + +# Only show warnings with the listed confidence levels. Leave empty to show +# all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED +confidence= + +# Disable the message, report, category or checker with the given id(s). You +# can either give multiple identifiers separated by comma (,) or put this +# option multiple times (only on the command line, not in the configuration +# file where it should appear only once).You can also use "--disable=all" to +# disable everything first and then reenable specific checks. For example, if +# you want to run only the similarities checker, you can use "--disable=all +# --enable=similarities". If you want to run only the classes checker, but have +# no Warning level messages displayed, use"--disable=all --enable=classes +# --disable=W" +disable=print-statement,parameter-unpacking,unpacking-in-except,old-raise-syntax,backtick,long-suffix,old-ne-operator,old-octal-literal,import-star-module-level,raw-checker-failed,bad-inline-option,locally-disabled,locally-enabled,file-ignored,suppressed-message,useless-suppression,deprecated-pragma,apply-builtin,basestring-builtin,buffer-builtin,cmp-builtin,coerce-builtin,execfile-builtin,file-builtin,long-builtin,raw_input-builtin,reduce-builtin,standarderror-builtin,unicode-builtin,xrange-builtin,coerce-method,delslice-method,getslice-method,setslice-method,no-absolute-import,old-division,dict-iter-method,dict-view-method,next-method-called,metaclass-assignment,indexing-exception,raising-string,reload-builtin,oct-method,hex-method,nonzero-method,cmp-method,input-builtin,round-builtin,intern-builtin,unichr-builtin,map-builtin-not-iterating,zip-builtin-not-iterating,range-builtin-not-iterating,filter-builtin-not-iterating,using-cmp-argument,eq-without-hash,div-method,idiv-method,rdiv-method,exception-message-attribute,invalid-str-codec,sys-max-int,bad-python3-import,deprecated-string-function,deprecated-str-translate-call + +# Enable the message, report, category or checker with the given id(s). You can +# either give multiple identifier separated by comma (,) or put this option +# multiple time (only on the command line, not in the configuration file where +# it should appear only once). See also the "--disable" option for examples. +enable= + + +[REPORTS] + +# Python expression which should return a note less than 10 (10 is the highest +# note). You have access to the variables errors warning, statement which +# respectively contain the number of errors / warnings messages and the total +# number of statements analyzed. This is used by the global evaluation report +# (RP0004). +evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) + +# Template used to display messages. This is a python new-style format string +# used to format the message information. See doc for all details +#msg-template= + +# Set the output format. Available formats are text, parseable, colorized, json +# and msvs (visual studio).You can also give a reporter class, eg +# mypackage.mymodule.MyReporterClass. +output-format=text + +# Tells whether to display a full report or only the messages +reports=no + +# Activate the evaluation score. +score=yes + + +[REFACTORING] + +# Maximum number of nested blocks for function / method body +max-nested-blocks=5 + + +[FORMAT] + +# Expected format of line ending, e.g. empty (any line ending), LF or CRLF. +expected-line-ending-format= + +# Regexp for a line that is allowed to be longer than the limit. +ignore-long-lines=^\s*(# )??$ + +# Number of spaces of indent required inside a hanging or continued line. +indent-after-paren=4 + +# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 +# tab). +indent-string=' ' + +# Maximum number of characters on a single line. +max-line-length=100 + +# Maximum number of lines in a module +max-module-lines=1000 + +# List of optional constructs for which whitespace checking is disabled. `dict- +# separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. +# `trailing-comma` allows a space between comma and closing bracket: (a, ). +# `empty-line` allows space-only lines. +no-space-check=trailing-comma,dict-separator + +# Allow the body of a class to be on the same line as the declaration if body +# contains single statement. +single-line-class-stmt=no + +# Allow the body of an if to be on the same line as the test if there is no +# else. +single-line-if-stmt=no + + +[SPELLING] + +# Spelling dictionary name. Available dictionaries: none. To make it working +# install python-enchant package. +spelling-dict= + +# List of comma separated words that should not be checked. +spelling-ignore-words= + +# A path to a file that contains private dictionary; one word per line. +spelling-private-dict-file= + +# Tells whether to store unknown words to indicated private dictionary in +# --spelling-private-dict-file option instead of raising a message. +spelling-store-unknown-words=no + + +[VARIABLES] + +# List of additional names supposed to be defined in builtins. Remember that +# you should avoid to define new builtins when possible. +additional-builtins= + +# Tells whether unused global variables should be treated as a violation. +allow-global-unused-variables=yes + +# List of strings which can identify a callback function by name. A callback +# name must start or end with one of those strings. +callbacks=cb_,_cb + +# A regular expression matching the name of dummy variables (i.e. expectedly +# not used). +dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ + +# Argument names that match this expression will be ignored. Default to name +# with leading underscore +ignored-argument-names=_.*|^ignored_|^unused_ + +# Tells whether we should check for unused import in __init__ files. +init-import=no + +# List of qualified module names which can have objects that can redefine +# builtins. +redefining-builtins-modules=six.moves,future.builtins + + +[SIMILARITIES] + +# Ignore comments when computing similarities. +ignore-comments=yes + +# Ignore docstrings when computing similarities. +ignore-docstrings=yes + +# Ignore imports when computing similarities. +ignore-imports=no + +# Minimum lines number of a similarity. +min-similarity-lines=4 + + +[BASIC] + +# Naming hint for argument names +argument-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct argument names +argument-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Naming hint for attribute names +attr-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct attribute names +attr-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Bad variable names which should always be refused, separated by a comma +bad-names=foo,bar,baz,toto,tutu,tata + +# Naming hint for class attribute names +class-attribute-name-hint=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Regular expression matching correct class attribute names +class-attribute-rgx=([A-Za-z_][A-Za-z0-9_]{2,30}|(__.*__))$ + +# Naming hint for class names +class-name-hint=[A-Z_][a-zA-Z0-9]+$ + +# Regular expression matching correct class names +class-rgx=[A-Z_][a-zA-Z0-9]+$ + +# Naming hint for constant names +const-name-hint=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Regular expression matching correct constant names +const-rgx=(([A-Z_][A-Z0-9_]*)|(__.*__))$ + +# Minimum line length for functions/classes that require docstrings, shorter +# ones are exempt. +docstring-min-length=-1 + +# Naming hint for function names +function-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct function names +function-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Good variable names which should always be accepted, separated by a comma +good-names=i,j,k,ex,Run,_ + +# Include a hint for the correct naming format with invalid-name +include-naming-hint=no + +# Naming hint for inline iteration names +inlinevar-name-hint=[A-Za-z_][A-Za-z0-9_]*$ + +# Regular expression matching correct inline iteration names +inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$ + +# Naming hint for method names +method-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct method names +method-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Naming hint for module names +module-name-hint=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Regular expression matching correct module names +module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$ + +# Colon-delimited sets of names that determine each other's naming style when +# the name regexes allow several styles. +name-group= + +# Regular expression which should only match function or class names that do +# not require a docstring. +no-docstring-rgx=^_ + +# List of decorators that produce properties, such as abc.abstractproperty. Add +# to this list to register other decorators that produce valid properties. +property-classes=abc.abstractproperty + +# Naming hint for variable names +variable-name-hint=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + +# Regular expression matching correct variable names +variable-rgx=(([a-z][a-z0-9_]{2,30})|(_[a-z0-9_]*))$ + + +[MISCELLANEOUS] + +# List of note tags to take in consideration, separated by a comma. +notes=FIXME,XXX,TODO + + +[TYPECHECK] + +# List of decorators that produce context managers, such as +# contextlib.contextmanager. Add to this list to register other decorators that +# produce valid context managers. +contextmanager-decorators=contextlib.contextmanager + +# List of members which are set dynamically and missed by pylint inference +# system, and so shouldn't trigger E1101 when accessed. Python regular +# expressions are accepted. +generated-members= + +# Tells whether missing members accessed in mixin class should be ignored. A +# mixin class is detected if its name ends with "mixin" (case insensitive). +ignore-mixin-members=yes + +# This flag controls whether pylint should warn about no-member and similar +# checks whenever an opaque object is returned when inferring. The inference +# can return multiple potential results while evaluating a Python object, but +# some branches might not be evaluated, which results in partial inference. In +# that case, it might be useful to still emit no-member and other checks for +# the rest of the inferred objects. +ignore-on-opaque-inference=yes + +# List of class names for which member attributes should not be checked (useful +# for classes with dynamically set attributes). This supports the use of +# qualified names. +ignored-classes=optparse.Values,thread._local,_thread._local + +# List of module names for which member attributes should not be checked +# (useful for modules/projects where namespaces are manipulated during runtime +# and thus existing member attributes cannot be deduced by static analysis. It +# supports qualified module names, as well as Unix pattern matching. +ignored-modules= + +# Show a hint with possible names when a member name was not found. The aspect +# of finding the hint is based on edit distance. +missing-member-hint=yes + +# The minimum edit distance a name should have in order to be considered a +# similar match for a missing member name. +missing-member-hint-distance=1 + +# The total number of similar names that should be taken in consideration when +# showing a hint for a missing member. +missing-member-max-choices=1 + + +[LOGGING] + +# Logging modules to check that the string format arguments are in logging +# function parameter format +logging-modules=logging + + +[IMPORTS] + +# Allow wildcard imports from modules that define __all__. +allow-wildcard-with-all=no + +# Analyse import fallback blocks. This can be used to support both Python 2 and +# 3 compatible code, which means that the block might have code that exists +# only in one or another interpreter, leading to false positives when analysed. +analyse-fallback-blocks=no + +# Deprecated modules which should not be used, separated by a comma +deprecated-modules=optparse,tkinter.tix + +# Create a graph of external dependencies in the given file (report RP0402 must +# not be disabled) +ext-import-graph= + +# Create a graph of every (i.e. internal and external) dependencies in the +# given file (report RP0402 must not be disabled) +import-graph= + +# Create a graph of internal dependencies in the given file (report RP0402 must +# not be disabled) +int-import-graph= + +# Force import order to recognize a module as part of the standard +# compatibility libraries. +known-standard-library= + +# Force import order to recognize a module as part of a third party library. +known-third-party=enchant + + +[CLASSES] + +# List of method names used to declare (i.e. assign) instance attributes. +defining-attr-methods=__init__,__new__,setUp + +# List of member names, which should be excluded from the protected access +# warning. +exclude-protected=_asdict,_fields,_replace,_source,_make + +# List of valid names for the first argument in a class method. +valid-classmethod-first-arg=cls + +# List of valid names for the first argument in a metaclass class method. +valid-metaclass-classmethod-first-arg=mcs + + +[DESIGN] + +# Maximum number of arguments for function / method +max-args=5 + +# Maximum number of attributes for a class (see R0902). +max-attributes=7 + +# Maximum number of boolean expressions in a if statement +max-bool-expr=5 + +# Maximum number of branch for function / method body +max-branches=12 + +# Maximum number of locals for function / method body +max-locals=15 + +# Maximum number of parents for a class (see R0901). +max-parents=7 + +# Maximum number of public methods for a class (see R0904). +max-public-methods=20 + +# Maximum number of return / yield for function / method body +max-returns=6 + +# Maximum number of statements in function / method body +max-statements=50 + +# Minimum number of public methods for a class (see R0903). +min-public-methods=2 + + +[EXCEPTIONS] + +# Exceptions that will emit a warning when being caught. Defaults to +# "Exception" +overgeneral-exceptions=Exception diff --git a/tests/test_app.py b/tests/test_app.py index 5cb2a18..9f14cc7 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -1,10 +1,15 @@ from __future__ import absolute_import, division, print_function import unittest from pirant import DevRant -from mock import patch +try: + from mock import patch +except ImportError: + from unittest.mock import patch + import json from pirant.utils import MockHttpResponse + class TestApp(unittest.TestCase): def setUp(self): self.devrant = DevRant() diff --git a/tests/test_handlers.py b/tests/test_handlers.py index ef9cece..8ea43d6 100644 --- a/tests/test_handlers.py +++ b/tests/test_handlers.py @@ -1,10 +1,15 @@ from __future__ import absolute_import, division, print_function import unittest -from mock import Mock, patch +try: + from mock import patch, Mock +except ImportError: + from unittest.mock import patch, Mock + from pirant.handlers import RequestHandler, ResponseHandler import json from pirant.utils import MockHttpResponse + class TestRequestHandler(unittest.TestCase): """ Unit tests for RequestHandler class