├── __init__.py ├── .gitattributes ├── requirements.txt ├── jarvis-bot.json ├── zappa_settings.json ├── request_jarvis.py ├── config.py ├── LICENSE ├── .gitignore ├── README.md ├── bot.py ├── app.py └── .pylintrc /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | botbuilder-core>=4.5.0.b4 2 | botbuilder-schema==4.5.0b4 3 | flask>=1.0.3 4 | requests -------------------------------------------------------------------------------- /jarvis-bot.json: -------------------------------------------------------------------------------- 1 | { 2 | "bot_name": "jarvis-bot", 3 | "bot_description": "This bot works with the deployed jarvis rest service to predict the queue for the input client emails." 4 | } -------------------------------------------------------------------------------- /zappa_settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "dev": { 3 | "app_function": "app.APP", 4 | "aws_region": "eu-west-1", 5 | "profile_name": "default", 6 | "project_name": "jarvis-bot", 7 | "runtime": "python3.7", 8 | "s3_bucket": "jarvis-bot" 9 | } 10 | } -------------------------------------------------------------------------------- /request_jarvis.py: -------------------------------------------------------------------------------- 1 | import json 2 | import requests 3 | 4 | 5 | def jarvis(email): 6 | jarvis_enpoint = {LINK TO THE HOSTED END POINT} 7 | 8 | headers = { 9 | 'Content-Type': 'application/json' 10 | } 11 | payload = { 12 | 'email':email 13 | } 14 | result = requests.post(jarvis_enpoint, headers=headers, data=json.dumps(payload)) 15 | result = json.loads((result.text)) 16 | return result 17 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) Microsoft Corporation. All rights reserved. 3 | # Licensed under the MIT License. 4 | 5 | import os 6 | 7 | class DefaultConfig: 8 | """ Bot Configuration """ 9 | 10 | PORT = 8080 11 | APP_ID = os.environ.get("MicrosoftAppId", "{APP ID AFTER REGISTERING THE APP ON AZURE}") 12 | APP_PASSWORD = os.environ.get("MicrosoftAppPassword", "{APP PASSWORD REGISTERING THE APP ON AZURE}") 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Operations 2.0 / jarvis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | . 2 | .. 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | build/ 14 | develop-eggs/ 15 | dist/ 16 | downloads/ 17 | eggs/ 18 | .eggs/ 19 | lib/ 20 | lib64/ 21 | parts/ 22 | sdist/ 23 | var/ 24 | wheels/ 25 | pip-wheel-metadata/ 26 | share/python-wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .nox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | *.py,cover 53 | .hypothesis/ 54 | .pytest_cache/ 55 | cover/ 56 | 57 | # Translations 58 | *.mo 59 | *.pot 60 | 61 | # Django stuff: 62 | *.log 63 | local_settings.py 64 | db.sqlite3 65 | db.sqlite3-journal 66 | 67 | # Flask stuff: 68 | instance/ 69 | .webassets-cache 70 | 71 | # Scrapy stuff: 72 | .scrapy 73 | 74 | # Sphinx documentation 75 | docs/_build/ 76 | 77 | # PyBuilder 78 | target/ 79 | 80 | # Jupyter Notebook 81 | .ipynb_checkpoints 82 | 83 | # VS Code 84 | .vscode/ 85 | 86 | # IPython 87 | profile_default/ 88 | ipython_config.py 89 | 90 | # pyenv 91 | # For a library or package, you might want to ignore these files since the code is 92 | # intended to run in multiple environments; otherwise, check them in: 93 | # .python-version 94 | 95 | # pipenv 96 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 97 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 98 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 99 | # install all needed dependencies. 100 | #Pipfile.lock 101 | 102 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 103 | __pypackages__/ 104 | 105 | # Celery stuff 106 | celerybeat-schedule 107 | celerybeat.pid 108 | 109 | # SageMath parsed files 110 | *.sage.py 111 | 112 | # Environments 113 | .env 114 | .venv 115 | env/ 116 | venv/ 117 | ENV/ 118 | env.bak/ 119 | venv.bak/ 120 | 121 | # Spyder project settings 122 | .spyderproject 123 | .spyproject 124 | 125 | # Rope project settings 126 | .ropeproject 127 | 128 | # mkdocs documentation 129 | /site 130 | 131 | # mypy 132 | .mypy_cache/ 133 | .dmypy.json 134 | dmypy.json 135 | 136 | # Pyre type checker 137 | .pyre/ 138 | 139 | # pytype static type analyzer 140 | .pytype/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Jarvis Bot 2 | 3 | This repository contains the code for the `Bot` created for Jarvis service. 4 | 5 | This repo will be used to integrate the **Jarvis Service** with **MS Teams** and other possible channels in the future. 6 | 7 | Details of the Bot: 8 | 9 | * Language: Python BotBuilder SDK 10 | * Link: [Bot Builder](https://github.com/microsoft/botbuilder-python) 11 | * Refrencing Web Service: [Jarvis](https://git.mdevlab.com/operations2.0/jarvis/jarvis-mark-2-service) 12 | 13 | 14 | 15 | ## Additional Information 16 | 17 | This bot has been created using [Bot Framework](https://dev.botframework.com), it shows how to create a simple bot that accepts input from the user and echoes it back. 18 | 19 | ## Prerequisites 20 | 21 | This sample **requires** prerequisites in order to run. 22 | 23 | ### Install Python 3.6 24 | 25 | ## Running the sample 26 | 27 | - Run `pip install -r requirements.txt` to install all dependencies 28 | - Run `python app.py` 29 | - Alternatively to the last command, you can set the file in an environment variable with `set FLASK_APP=app.py` in windows (`export FLASK_APP=app.py` in mac/linux) and then run `flask run --host=127.0.0.1 --port=3978` 30 | 31 | 32 | ## Testing the bot using Bot Framework Emulator 33 | 34 | [Bot Framework Emulator](https://github.com/microsoft/botframework-emulator) is a desktop application that allows bot developers to test and debug their bots on localhost or running remotely through a tunnel. 35 | 36 | - Install the Bot Framework Emulator version 4.3.0 or greater from [here](https://github.com/Microsoft/BotFramework-Emulator/releases) 37 | 38 | ### Connect to the bot using Bot Framework Emulator 39 | 40 | - Launch Bot Framework Emulator 41 | - Enter a Bot URL of `http://localhost:3978/api/messages` 42 | 43 | 44 | ## Further reading 45 | 46 | - [Bot Framework Documentation](https://docs.botframework.com) 47 | - [Bot Basics](https://docs.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0) 48 | - [Dialogs](https://docs.microsoft.com/azure/bot-service/bot-builder-concept-dialog?view=azure-bot-service-4.0) 49 | - [Gathering Input Using Prompts](https://docs.microsoft.com/azure/bot-service/bot-builder-prompts?view=azure-bot-service-4.0&tabs=csharp) 50 | - [Activity processing](https://docs.microsoft.com/en-us/azure/bot-service/bot-builder-concept-activity-processing?view=azure-bot-service-4.0) 51 | - [Azure Bot Service Introduction](https://docs.microsoft.com/azure/bot-service/bot-service-overview-introduction?view=azure-bot-service-4.0) 52 | - [Azure Bot Service Documentation](https://docs.microsoft.com/azure/bot-service/?view=azure-bot-service-4.0) 53 | - [Azure CLI](https://docs.microsoft.com/cli/azure/?view=azure-cli-latest) 54 | - [Azure Portal](https://portal.azure.com) 55 | - [Language Understanding using LUIS](https://docs.microsoft.com/azure/cognitive-services/luis/) 56 | - [Channels and Bot Connector Service](https://docs.microsoft.com/azure/bot-service/bot-concepts?view=azure-bot-service-4.0) -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT License. 3 | 4 | from botbuilder.core import ActivityHandler, TurnContext 5 | from botbuilder.schema import ChannelAccount 6 | from request_jarvis import jarvis 7 | 8 | 9 | 10 | class MyBot(ActivityHandler): 11 | # See https://aka.ms/about-bot-activity-message to learn more about the message and other activity types. 12 | 13 | async def on_message_activity(self, turn_context: TurnContext): 14 | 15 | if turn_context.activity.type == "message" and turn_context.activity.text: 16 | # Check to see if the user wants to initiate a conversation/prediction. 17 | if turn_context.activity.text.lower() == "hi" or turn_context.activity.text.lower() == "hey" or turn_context.activity.text.lower() == "initiate" or turn_context.activity.text.lower() == "restart": 18 | await turn_context.send_activity("Hi... I'm **Jarvis**. Whatever you say I'll suggest a suitable category for it. I have been trained on historic emails that our support teams have received from clients. I very recently joined and still have lot to learn. Please expect some errors. :)") 19 | await turn_context.send_activity('Why dont you type your query below for me and i will recommend you probabilites of the queue.') 20 | # Check to see if the user sent a simple "quit" message. 21 | elif turn_context.activity.text.lower() == "quit": 22 | # Send a reply. 23 | await turn_context.send_activity("It was nice working with you! Feel free to reach out to me! Bye!") 24 | exit(0) 25 | else: 26 | # Echo the message text back to the user. And give them a prediction 27 | await turn_context.send_activity(f"You said '{ turn_context.activity.text }'") 28 | await turn_context.send_activity(f"My training tells me that this email can be sent to:") 29 | result = jarvis(turn_context.activity.text) 30 | for key, value in result.items(): 31 | await turn_context.send_activity(f"Queue **{key}** with a probability of ***{round(value, 2)}%***") 32 | await turn_context.send_activity(f"Please type in another query to predict or type quit to exit") 33 | 34 | async def on_members_added_activity( 35 | self, 36 | members_added: ChannelAccount, 37 | turn_context: TurnContext 38 | ): 39 | for member_added in members_added: 40 | if member_added.id != turn_context.activity.recipient.id: 41 | await turn_context.send_activity("**Hello and welcome!**") 42 | await turn_context.send_activity("Hi... I'm **Jarvis**. Whatever you say I'll suggest a suitable category for it. I have been trained on historic emails that our support teams have received from clients. I very recently joined and still have lot to learn. Please expect some errors. :)") 43 | await turn_context.send_activity('Why dont you type your query below for me and i will recommend you probabilites of the queue.') 44 | 45 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) Microsoft Corporation. All rights reserved. 2 | # Licensed under the MIT License. 3 | 4 | import asyncio 5 | import sys 6 | from datetime import datetime 7 | from types import MethodType 8 | 9 | from flask import Flask, request, Response 10 | from botbuilder.core import ( 11 | BotFrameworkAdapter, 12 | BotFrameworkAdapterSettings, 13 | TurnContext, 14 | ) 15 | from botbuilder.schema import Activity, ActivityTypes 16 | from bot import MyBot 17 | 18 | # Create the loop and Flask app 19 | LOOP = asyncio.get_event_loop() 20 | APP = Flask(__name__, instance_relative_config=True) 21 | APP.config.from_object("config.DefaultConfig") 22 | 23 | # Create adapter. 24 | # See https://aka.ms/about-bot-adapter to learn more about how bots work. 25 | SETTINGS = BotFrameworkAdapterSettings(APP.config["APP_ID"], APP.config["APP_PASSWORD"]) 26 | ADAPTER = BotFrameworkAdapter(SETTINGS) 27 | 28 | # Catch-all for errors. 29 | # pylint: disable=unused-argument 30 | async def on_error(self, context: TurnContext, error: Exception): 31 | # This check writes out errors to console log .vs. app insights. 32 | # NOTE: In production environment, you should consider logging this to Azure 33 | # application insights. 34 | print(f"\n [on_turn_error] unhandled error: {error}", file=sys.stderr) 35 | 36 | # Send a message to the user 37 | await context.send_activity("The bot encounted an error or bug.") 38 | await context.send_activity("To continue to run this bot, please fix the bot source code.") 39 | # Send a trace activity if we're talking to the Bot Framework Emulator 40 | if context.activity.channel_id == 'emulator': 41 | # Create a trace activity that contains the error object 42 | trace_activity = Activity( 43 | label="TurnError", 44 | name="on_turn_error Trace", 45 | timestamp=datetime.utcnow(), 46 | type=ActivityTypes.trace, 47 | value=f"{error}", 48 | value_type="https://www.botframework.com/schemas/error" 49 | ) 50 | # Send a trace activity, which will be displayed in Bot Framework Emulator 51 | await context.send_activity(trace_activity) 52 | 53 | ADAPTER.on_turn_error = MethodType(on_error, ADAPTER) 54 | 55 | # Create the main dialog 56 | BOT = MyBot() 57 | 58 | # Listen for incoming requests on /api/messages. 59 | @APP.route("/api/messages", methods=["POST"]) 60 | def messages(): 61 | # Main bot message handler. 62 | if "application/json" in request.headers["Content-Type"]: 63 | body = request.json 64 | else: 65 | return Response(status=415) 66 | 67 | activity = Activity().deserialize(body) 68 | auth_header = ( 69 | request.headers["Authorization"] if "Authorization" in request.headers else "" 70 | ) 71 | 72 | try: 73 | task = LOOP.create_task( 74 | ADAPTER.process_activity(activity, auth_header, BOT.on_turn) 75 | ) 76 | LOOP.run_until_complete(task) 77 | return Response(status=201) 78 | except Exception as exception: 79 | raise exception 80 | 81 | 82 | if __name__ == "__main__": 83 | try: 84 | APP.run(debug=True, port=APP.config["PORT"]) # nosec debug 85 | except Exception as exception: 86 | raise exception 87 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | 3 | # A comma-separated list of package or module names from where C extensions may 4 | # be loaded. Extensions are loading into the active Python interpreter and may 5 | # run arbitrary code. 6 | extension-pkg-whitelist= 7 | 8 | # Add files or directories to the blacklist. They should be base names, not 9 | # paths. 10 | ignore= 11 | 12 | # Add files or directories matching the regex patterns to the blacklist. The 13 | # regex matches against base names, not paths. 14 | ignore-patterns= 15 | 16 | # Python code to execute, usually for sys.path manipulation such as 17 | # pygtk.require(). 18 | #init-hook= 19 | 20 | # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the 21 | # number of processors available to use. 22 | jobs=1 23 | 24 | # Control the amount of potential inferred values when inferring a single 25 | # object. This can help the performance when dealing with large functions or 26 | # complex, nested conditions. 27 | limit-inference-results=100 28 | 29 | # List of plugins (as comma separated values of python modules names) to load, 30 | # usually to register additional checkers. 31 | load-plugins= 32 | 33 | # Pickle collected data for later comparisons. 34 | persistent=yes 35 | 36 | # Specify a configuration file. 37 | #rcfile= 38 | 39 | # When enabled, pylint would attempt to guess common misconfiguration and emit 40 | # user-friendly hints instead of false-positive error messages. 41 | suggestion-mode=yes 42 | 43 | # Allow loading of arbitrary C extensions. Extensions are imported into the 44 | # active Python interpreter and may run arbitrary code. 45 | unsafe-load-any-extension=no 46 | 47 | 48 | [MESSAGES CONTROL] 49 | 50 | # Only show warnings with the listed confidence levels. Leave empty to show 51 | # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. 52 | confidence= 53 | 54 | # Disable the message, report, category or checker with the given id(s). You 55 | # can either give multiple identifiers separated by comma (,) or put this 56 | # option multiple times (only on the command line, not in the configuration 57 | # file where it should appear only once). You can also use "--disable=all" to 58 | # disable everything first and then reenable specific checks. For example, if 59 | # you want to run only the similarities checker, you can use "--disable=all 60 | # --enable=similarities". If you want to run only the classes checker, but have 61 | # no Warning level messages displayed, use "--disable=all --enable=classes 62 | # --disable=W". 63 | disable=missing-docstring, 64 | too-few-public-methods, 65 | bad-continuation, 66 | no-self-use, 67 | duplicate-code, 68 | broad-except 69 | 70 | # Enable the message, report, category or checker with the given id(s). You can 71 | # either give multiple identifier separated by comma (,) or put this option 72 | # multiple time (only on the command line, not in the configuration file where 73 | # it should appear only once). See also the "--disable" option for examples. 74 | enable=c-extension-no-member 75 | 76 | 77 | [REPORTS] 78 | 79 | # Python expression which should return a note less than 10 (10 is the highest 80 | # note). You have access to the variables errors warning, statement which 81 | # respectively contain the number of errors / warnings messages and the total 82 | # number of statements analyzed. This is used by the global evaluation report 83 | # (RP0004). 84 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) 85 | 86 | # Template used to display messages. This is a python new-style format string 87 | # used to format the message information. See doc for all details. 88 | #msg-template= 89 | 90 | # Set the output format. Available formats are text, parseable, colorized, json 91 | # and msvs (visual studio). You can also give a reporter class, e.g. 92 | # mypackage.mymodule.MyReporterClass. 93 | output-format=text 94 | 95 | # Tells whether to display a full report or only the messages. 96 | reports=no 97 | 98 | # Activate the evaluation score. 99 | score=yes 100 | 101 | 102 | [REFACTORING] 103 | 104 | # Maximum number of nested blocks for function / method body 105 | max-nested-blocks=5 106 | 107 | # Complete name of functions that never returns. When checking for 108 | # inconsistent-return-statements if a never returning function is called then 109 | # it will be considered as an explicit return statement and no message will be 110 | # printed. 111 | never-returning-functions=sys.exit 112 | 113 | 114 | [LOGGING] 115 | 116 | # Format style used to check logging format string. `old` means using % 117 | # formatting, while `new` is for `{}` formatting. 118 | logging-format-style=old 119 | 120 | # Logging modules to check that the string format arguments are in logging 121 | # function parameter format. 122 | logging-modules=logging 123 | 124 | 125 | [SPELLING] 126 | 127 | # Limits count of emitted suggestions for spelling mistakes. 128 | max-spelling-suggestions=4 129 | 130 | # Spelling dictionary name. Available dictionaries: none. To make it working 131 | # install python-enchant package.. 132 | spelling-dict= 133 | 134 | # List of comma separated words that should not be checked. 135 | spelling-ignore-words= 136 | 137 | # A path to a file that contains private dictionary; one word per line. 138 | spelling-private-dict-file= 139 | 140 | # Tells whether to store unknown words to indicated private dictionary in 141 | # --spelling-private-dict-file option instead of raising a message. 142 | spelling-store-unknown-words=no 143 | 144 | 145 | [MISCELLANEOUS] 146 | 147 | # List of note tags to take in consideration, separated by a comma. 148 | notes=FIXME, 149 | XXX, 150 | TODO 151 | 152 | 153 | [TYPECHECK] 154 | 155 | # List of decorators that produce context managers, such as 156 | # contextlib.contextmanager. Add to this list to register other decorators that 157 | # produce valid context managers. 158 | contextmanager-decorators=contextlib.contextmanager 159 | 160 | # List of members which are set dynamically and missed by pylint inference 161 | # system, and so shouldn't trigger E1101 when accessed. Python regular 162 | # expressions are accepted. 163 | generated-members= 164 | 165 | # Tells whether missing members accessed in mixin class should be ignored. A 166 | # mixin class is detected if its name ends with "mixin" (case insensitive). 167 | ignore-mixin-members=yes 168 | 169 | # Tells whether to warn about missing members when the owner of the attribute 170 | # is inferred to be None. 171 | ignore-none=yes 172 | 173 | # This flag controls whether pylint should warn about no-member and similar 174 | # checks whenever an opaque object is returned when inferring. The inference 175 | # can return multiple potential results while evaluating a Python object, but 176 | # some branches might not be evaluated, which results in partial inference. In 177 | # that case, it might be useful to still emit no-member and other checks for 178 | # the rest of the inferred objects. 179 | ignore-on-opaque-inference=yes 180 | 181 | # List of class names for which member attributes should not be checked (useful 182 | # for classes with dynamically set attributes). This supports the use of 183 | # qualified names. 184 | ignored-classes=optparse.Values,thread._local,_thread._local 185 | 186 | # List of module names for which member attributes should not be checked 187 | # (useful for modules/projects where namespaces are manipulated during runtime 188 | # and thus existing member attributes cannot be deduced by static analysis. It 189 | # supports qualified module names, as well as Unix pattern matching. 190 | ignored-modules= 191 | 192 | # Show a hint with possible names when a member name was not found. The aspect 193 | # of finding the hint is based on edit distance. 194 | missing-member-hint=yes 195 | 196 | # The minimum edit distance a name should have in order to be considered a 197 | # similar match for a missing member name. 198 | missing-member-hint-distance=1 199 | 200 | # The total number of similar names that should be taken in consideration when 201 | # showing a hint for a missing member. 202 | missing-member-max-choices=1 203 | 204 | 205 | [VARIABLES] 206 | 207 | # List of additional names supposed to be defined in builtins. Remember that 208 | # you should avoid defining new builtins when possible. 209 | additional-builtins= 210 | 211 | # Tells whether unused global variables should be treated as a violation. 212 | allow-global-unused-variables=yes 213 | 214 | # List of strings which can identify a callback function by name. A callback 215 | # name must start or end with one of those strings. 216 | callbacks=cb_, 217 | _cb 218 | 219 | # A regular expression matching the name of dummy variables (i.e. expected to 220 | # not be used). 221 | dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ 222 | 223 | # Argument names that match this expression will be ignored. Default to name 224 | # with leading underscore. 225 | ignored-argument-names=_.*|^ignored_|^unused_ 226 | 227 | # Tells whether we should check for unused import in __init__ files. 228 | init-import=no 229 | 230 | # List of qualified module names which can have objects that can redefine 231 | # builtins. 232 | redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io 233 | 234 | 235 | [FORMAT] 236 | 237 | # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. 238 | expected-line-ending-format= 239 | 240 | # Regexp for a line that is allowed to be longer than the limit. 241 | ignore-long-lines=^\s*(# )??$ 242 | 243 | # Number of spaces of indent required inside a hanging or continued line. 244 | indent-after-paren=4 245 | 246 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 247 | # tab). 248 | indent-string=' ' 249 | 250 | # Maximum number of characters on a single line. 251 | max-line-length=120 252 | 253 | # Maximum number of lines in a module. 254 | max-module-lines=1000 255 | 256 | # List of optional constructs for which whitespace checking is disabled. `dict- 257 | # separator` is used to allow tabulation in dicts, etc.: {1 : 1,\n222: 2}. 258 | # `trailing-comma` allows a space between comma and closing bracket: (a, ). 259 | # `empty-line` allows space-only lines. 260 | no-space-check=trailing-comma, 261 | dict-separator 262 | 263 | # Allow the body of a class to be on the same line as the declaration if body 264 | # contains single statement. 265 | single-line-class-stmt=no 266 | 267 | # Allow the body of an if to be on the same line as the test if there is no 268 | # else. 269 | single-line-if-stmt=no 270 | 271 | 272 | [SIMILARITIES] 273 | 274 | # Ignore comments when computing similarities. 275 | ignore-comments=yes 276 | 277 | # Ignore docstrings when computing similarities. 278 | ignore-docstrings=yes 279 | 280 | # Ignore imports when computing similarities. 281 | ignore-imports=no 282 | 283 | # Minimum lines number of a similarity. 284 | min-similarity-lines=4 285 | 286 | 287 | [BASIC] 288 | 289 | # Naming style matching correct argument names. 290 | argument-naming-style=snake_case 291 | 292 | # Regular expression matching correct argument names. Overrides argument- 293 | # naming-style. 294 | #argument-rgx= 295 | 296 | # Naming style matching correct attribute names. 297 | attr-naming-style=snake_case 298 | 299 | # Regular expression matching correct attribute names. Overrides attr-naming- 300 | # style. 301 | #attr-rgx= 302 | 303 | # Bad variable names which should always be refused, separated by a comma. 304 | bad-names=foo, 305 | bar, 306 | baz, 307 | toto, 308 | tutu, 309 | tata 310 | 311 | # Naming style matching correct class attribute names. 312 | class-attribute-naming-style=any 313 | 314 | # Regular expression matching correct class attribute names. Overrides class- 315 | # attribute-naming-style. 316 | #class-attribute-rgx= 317 | 318 | # Naming style matching correct class names. 319 | class-naming-style=PascalCase 320 | 321 | # Regular expression matching correct class names. Overrides class-naming- 322 | # style. 323 | #class-rgx= 324 | 325 | # Naming style matching correct constant names. 326 | const-naming-style=UPPER_CASE 327 | 328 | # Regular expression matching correct constant names. Overrides const-naming- 329 | # style. 330 | #const-rgx= 331 | 332 | # Minimum line length for functions/classes that require docstrings, shorter 333 | # ones are exempt. 334 | docstring-min-length=-1 335 | 336 | # Naming style matching correct function names. 337 | function-naming-style=snake_case 338 | 339 | # Regular expression matching correct function names. Overrides function- 340 | # naming-style. 341 | #function-rgx= 342 | 343 | # Good variable names which should always be accepted, separated by a comma. 344 | good-names=i, 345 | j, 346 | k, 347 | ex, 348 | Run, 349 | _ 350 | 351 | # Include a hint for the correct naming format with invalid-name. 352 | include-naming-hint=no 353 | 354 | # Naming style matching correct inline iteration names. 355 | inlinevar-naming-style=any 356 | 357 | # Regular expression matching correct inline iteration names. Overrides 358 | # inlinevar-naming-style. 359 | #inlinevar-rgx= 360 | 361 | # Naming style matching correct method names. 362 | method-naming-style=snake_case 363 | 364 | # Regular expression matching correct method names. Overrides method-naming- 365 | # style. 366 | #method-rgx= 367 | 368 | # Naming style matching correct module names. 369 | module-naming-style=snake_case 370 | 371 | # Regular expression matching correct module names. Overrides module-naming- 372 | # style. 373 | #module-rgx= 374 | 375 | # Colon-delimited sets of names that determine each other's naming style when 376 | # the name regexes allow several styles. 377 | name-group= 378 | 379 | # Regular expression which should only match function or class names that do 380 | # not require a docstring. 381 | no-docstring-rgx=^_ 382 | 383 | # List of decorators that produce properties, such as abc.abstractproperty. Add 384 | # to this list to register other decorators that produce valid properties. 385 | # These decorators are taken in consideration only for invalid-name. 386 | property-classes=abc.abstractproperty 387 | 388 | # Naming style matching correct variable names. 389 | variable-naming-style=snake_case 390 | 391 | # Regular expression matching correct variable names. Overrides variable- 392 | # naming-style. 393 | #variable-rgx= 394 | 395 | 396 | [STRING] 397 | 398 | # This flag controls whether the implicit-str-concat-in-sequence should 399 | # generate a warning on implicit string concatenation in sequences defined over 400 | # several lines. 401 | check-str-concat-over-line-jumps=no 402 | 403 | 404 | [IMPORTS] 405 | 406 | # Allow wildcard imports from modules that define __all__. 407 | allow-wildcard-with-all=no 408 | 409 | # Analyse import fallback blocks. This can be used to support both Python 2 and 410 | # 3 compatible code, which means that the block might have code that exists 411 | # only in one or another interpreter, leading to false positives when analysed. 412 | analyse-fallback-blocks=no 413 | 414 | # Deprecated modules which should not be used, separated by a comma. 415 | deprecated-modules=optparse,tkinter.tix 416 | 417 | # Create a graph of external dependencies in the given file (report RP0402 must 418 | # not be disabled). 419 | ext-import-graph= 420 | 421 | # Create a graph of every (i.e. internal and external) dependencies in the 422 | # given file (report RP0402 must not be disabled). 423 | import-graph= 424 | 425 | # Create a graph of internal dependencies in the given file (report RP0402 must 426 | # not be disabled). 427 | int-import-graph= 428 | 429 | # Force import order to recognize a module as part of the standard 430 | # compatibility libraries. 431 | known-standard-library= 432 | 433 | # Force import order to recognize a module as part of a third party library. 434 | known-third-party=enchant 435 | 436 | 437 | [CLASSES] 438 | 439 | # List of method names used to declare (i.e. assign) instance attributes. 440 | defining-attr-methods=__init__, 441 | __new__, 442 | setUp 443 | 444 | # List of member names, which should be excluded from the protected access 445 | # warning. 446 | exclude-protected=_asdict, 447 | _fields, 448 | _replace, 449 | _source, 450 | _make 451 | 452 | # List of valid names for the first argument in a class method. 453 | valid-classmethod-first-arg=cls 454 | 455 | # List of valid names for the first argument in a metaclass class method. 456 | valid-metaclass-classmethod-first-arg=cls 457 | 458 | 459 | [DESIGN] 460 | 461 | # Maximum number of arguments for function / method. 462 | max-args=5 463 | 464 | # Maximum number of attributes for a class (see R0902). 465 | max-attributes=7 466 | 467 | # Maximum number of boolean expressions in an if statement. 468 | max-bool-expr=5 469 | 470 | # Maximum number of branch for function / method body. 471 | max-branches=12 472 | 473 | # Maximum number of locals for function / method body. 474 | max-locals=15 475 | 476 | # Maximum number of parents for a class (see R0901). 477 | max-parents=7 478 | 479 | # Maximum number of public methods for a class (see R0904). 480 | max-public-methods=20 481 | 482 | # Maximum number of return / yield for function / method body. 483 | max-returns=6 484 | 485 | # Maximum number of statements in function / method body. 486 | max-statements=50 487 | 488 | # Minimum number of public methods for a class (see R0903). 489 | min-public-methods=2 490 | 491 | 492 | [EXCEPTIONS] 493 | 494 | # Exceptions that will emit a warning when being caught. Defaults to 495 | # "BaseException, Exception". 496 | overgeneral-exceptions=BaseException, 497 | Exception 498 | --------------------------------------------------------------------------------