"mqtt added"

This commit is contained in:
Manuel Fritsch 2021-03-14 11:52:46 +01:00
parent 9c3a1f564b
commit 12857c73ef
50 changed files with 10834 additions and 113 deletions

View file

@ -1 +1 @@
__version__ = "4.0.0.31"
__version__ = "4.0.0.32"

View file

@ -60,7 +60,7 @@ class Actor:
def __str__(self):
return "name={} props={}, state={}, type={}".format(self.name, self.props, self.state, self.type)
def to_dict(self):
return dict(id=self.id, name=self.name, type=self.type, props=self.props.to_dict(), state=self.instance.get_state())
return dict(id=self.id, name=self.name, type=self.type, props=self.props.to_dict(), state2="HELLO WORLD", state=self.instance.get_state())
@dataclass
@ -150,3 +150,12 @@ class NotificationAction:
def to_dict(self):
return dict(id=self.id, label=self.label)
class NotificationType(Enum):
INFO="info"
WARNING="warning"
ERROR="error"
SUCCESS="success"
def __str__(self):
return self.value

View file

@ -35,6 +35,7 @@ class CBPiSensor(CBPiBase, metaclass=ABCMeta):
def push_update(self, value):
try:
self.cbpi.ws.send(dict(topic="sensorstate", id=self.id, value=value))
self.cbpi.push_update("cbpi/sensor/{}/udpate".format(self.id), dict(id=self.id, value=value), retain=True)
except:
logging.error("Faild to push sensor update")

View file

@ -6,6 +6,12 @@ index_url: /cbpi_ui/static/index.html
port: 8000
mqtt: false
mqtt_host: localhost
mqtt_port: 1883
mqtt_username: ""
mqtt_password: ""
username: cbpi
password: 123

View file

@ -8,15 +8,14 @@ class ActorController(BasicController):
super(ActorController, self).__init__(cbpi, Actor,"actor.json")
self.update_key = "actorupdate"
async def on(self, id):
try:
item = self.find_by_id(id)
if item.instance.state is False:
await item.instance.on()
await self.push_udpate()
#await self.cbpi.satellite.publish("cbpi/actor/on", "ACTOR ON")
self.cbpi.push_update("cbpi/actor/"+id, item.to_dict(), True)
except Exception as e:
logging.error("Faild to switch on Actor {} {}".format(id, e))
@ -26,14 +25,16 @@ class ActorController(BasicController):
if item.instance.state is True:
await item.instance.off()
await self.push_udpate()
self.cbpi.push_update("cbpi/actor/"+id, item.to_dict())
except Exception as e:
logging.error("Faild to switch on Actor {} {}".format(id, e))
logging.error("Faild to switch on Actor {} {}".format(id, e), True)
async def toogle(self, id):
try:
item = self.find_by_id(id)
instance = item.get("instance")
await instance.toggle()
self.cbpi.push_update("cbpi/actor/update", item.to_dict())
except Exception as e:
logging.error("Faild to switch on Actor {} {}".format(id, e))

View file

@ -30,7 +30,6 @@ class BasicController:
await self.load()
def create(self, data):
return self.resource(data.get("id"), data.get("name"), type=data.get("type"), props=Props(data.get("props", {})) )
async def load(self):
@ -56,6 +55,7 @@ class BasicController:
async def push_udpate(self):
self.cbpi.ws.send(dict(topic=self.update_key, data=list(map(lambda item: item.to_dict(), self.data))))
self.cbpi.push_update("cbpi/{}/update".format(self.update_key), list(map(lambda item: item.to_dict(), self.data)))
def find_by_id(self, id):
return next((item for item in self.data if item.id == id), None)

View file

@ -1,3 +1,4 @@
from cbpi.api.dataclasses import NotificationType
import logging
import json
import os
@ -29,9 +30,10 @@ class DashboardController:
return {}
async def add_content(self, dashboard_id, data):
print(data)
with open(self.path, 'w') as outfile:
json.dump(data, outfile, indent=4, sort_keys=True)
self.cbpi.notify(title="Dashboard", message="Saved Successfully", type="success")
self.cbpi.notify(title="Dashboard", message="Saved Successfully", type=NotificationType.SUCCESS)
return {"status": "OK"}
async def delete_content(self, dashboard_id):

View file

@ -1,18 +1,36 @@
import asyncio
from cbpi.api.dataclasses import NotificationType
import logging
import shortuuid
class NotificationController:
def __init__(self, cbpi):
'''
:param cbpi: craftbeerpi object
'''
self.cbpi = cbpi
self.logger = logging.getLogger(__name__)
self.callback_cache = {}
self.listener = {}
def notify(self, title, message: str, type: str = "info", action=[]) -> None:
def add_listener(self, method):
listener_id = shortuuid.uuid()
self.listener[listener_id] = method
return listener_id
def remove_listener(self, listener_id):
try:
del self.listener[listener_id]
except:
self.logger.error("Faild to remove listener {}".format(listener_id))
async def _call_listener(self, title, message, type, action):
for id, method in self.listener.items():
print(id, method)
asyncio.create_task(method(self.cbpi, title, message, type, action ))
def notify(self, title, message: str, type: NotificationType = NotificationType.INFO, action=[]) -> None:
'''
This is a convinience method to send notification to the client
@ -29,7 +47,11 @@ class NotificationController:
actions = list(map(lambda item: prepare_action(item), action))
self.callback_cache[notifcation_id] = action
self.cbpi.ws.send(dict(id=notifcation_id, topic="notifiaction", type=type, title=title, message=message, action=actions))
self.cbpi.ws.send(dict(id=notifcation_id, topic="notifiaction", type=type.value, title=title, message=message, action=actions))
data = dict(type=type.value, title=title, message=message, action=actions)
self.cbpi.push_update(topic="cbpi/notification", data=data)
asyncio.create_task(self._call_listener(title, message, type, action))
def notify_callback(self, notification_id, action_id) -> None:
try:

View file

@ -1,38 +1,82 @@
import asyncio
from asyncio_mqtt import Client, MqttError, Will
import json
from re import M
from asyncio_mqtt import Client, MqttError, Will, client
from contextlib import AsyncExitStack, asynccontextmanager
from cbpi import __version__
import logging
class SatelliteController:
def __init__(self, cbpi):
self.cbpi = cbpi
self.logger = logging.getLogger(__name__)
self.host = cbpi.static_config.get("mqtt_host", "localhost")
self.port = cbpi.static_config.get("mqtt_port", 1883)
self.username = cbpi.static_config.get("mqtt_username", None)
self.password = cbpi.static_config.get("mqtt_password", None)
self.client = None
self.topic_filters = [
("cbpi/actor/+/on", self._actor_on),
("cbpi/actor/+/off", self._actor_off)
]
self.tasks = set()
async def init(self):
asyncio.create_task(self.init_client(self.cbpi))
async def publish(self, topic, message):
print("MQTT ON")
await self.client.publish(topic, message, qos=1)
async def publish(self, topic, message, retain=False):
if self.client is not None and self.client._connected:
try:
await self.client.publish(topic, message, qos=1, retain=retain)
except:
self.logger.warning("Faild to push data via mqtt")
async def handle_message(self, messages):
async def _actor_on(self, messages):
async for message in messages:
print("FILTERED", message.payload.decode())
try:
topic_key = message.topic.split("/")
await self.cbpi.actor.on(topic_key[2])
except:
self.logger.warning("Faild to process actor on via mqtt")
async def handle_unfilterd_message(self, messages):
async def _actor_off(self, messages):
async for message in messages:
print("UNFILTERED", message.payload.decode())
try:
topic_key = message.topic.split("/")
await self.cbpi.actor.off(topic_key[2])
except:
self.logger.warning("Faild to process actor off via mqtt")
def subcribe(self, topic, method):
task = asyncio.create_task(self._subcribe(topic, method))
return task
async def _subcribe(self, topic, method):
while True:
try:
if self.client._connected.done():
async with self.client.filtered_messages(topic) as messages:
await self.client.subscribe(topic)
async for message in messages:
await method(message.payload.decode())
except asyncio.CancelledError as e:
# Cancel
self.logger.warning(
"Sub CancelledError Exception: {}".format(e))
return
except MqttError as e:
self.logger.error("Sub MQTT Exception: {}".format(e))
except Exception as e:
self.logger.error("Sub Exception: {}".format(e))
# wait before try to resubscribe
await asyncio.sleep(5)
async def init_client(self, cbpi):
async def log_messages(messages, template):
async for message in messages:
print(template.format(message.payload.decode()))
async def cancel_tasks(tasks):
for task in tasks:
@ -44,31 +88,32 @@ class SatelliteController:
except asyncio.CancelledError:
pass
while True:
try:
async with AsyncExitStack() as stack:
self.tasks = set()
stack.push_async_callback(cancel_tasks, self.tasks)
self.client = Client(self.host, port=self.port, username=self.username, password=self.password, will=Will(topic="cbpi/diconnect", payload="CBPi Server Disconnected"))
tasks = set()
stack.push_async_callback(cancel_tasks, tasks)
self.client = Client("localhost", will=Will(topic="cbpi/diconnect", payload="CBPi Server Disconnected"))
await stack.enter_async_context(self.client)
topic_filters = (
"cbpi/sensor/#",
"cbpi/actor/#"
)
for topic_filter in topic_filters:
# Log all messages that matches the filter
manager = self.client.filtered_messages(topic_filter)
for topic_filter in self.topic_filters:
topic = topic_filter[0]
method = topic_filter[1]
manager = self.client.filtered_messages(topic)
messages = await stack.enter_async_context(manager)
task = asyncio.create_task(self.handle_message(messages))
tasks.add(task)
task = asyncio.create_task(method(messages))
self.tasks.add(task)
messages = await stack.enter_async_context(self.client.unfiltered_messages())
task = asyncio.create_task(self.handle_unfilterd_message(messages))
tasks.add(task)
for topic_filter in self.topic_filters:
topic = topic_filter[0]
await self.client.subscribe(topic)
await self.client.subscribe("cbpi/#")
await asyncio.gather(*tasks)
self.logger.info("MQTT Connected to {}:{}".format(self.host, self.port))
await asyncio.gather(*self.tasks)
except MqttError as e:
self.logger.error("MQTT Exception: {}".format(e))
except Exception as e:
self.logger.error("MQTT General Exception: {}".format(e))
await asyncio.sleep(5)

View file

@ -78,7 +78,6 @@ class StepController:
try:
type_cfg = self.types.get(item.type)
clazz = type_cfg.get("class")
print("CLASS", clazz)
item.instance = clazz(self.cbpi, item.id, item.name, item.props, self.done)
except Exception as e:
logging.warning("Failed to create step instance %s - %s " % (id, e))
@ -258,6 +257,8 @@ class StepController:
else:
self.cbpi.ws.send(dict(topic="step_update", data=list(map(lambda item: item.to_dict(), self.profile))))
self.cbpi.push_update(topic="cbpi/stepupdate", data=list(map(lambda item: item.to_dict(), self.profile)))
async def start_step(self,step):
try:
logging.info("Try to start step %s" % step)

View file

@ -1,4 +1,8 @@
import asyncio
import json
from voluptuous.schema_builder import message
from cbpi.api.dataclasses import NotificationType
from cbpi.controller.notification_controller import NotificationController
import logging
from os import urandom
@ -22,6 +26,7 @@ from cbpi.controller.sensor_controller import SensorController
from cbpi.controller.step_controller import StepController
from cbpi.controller.recipe_controller import RecipeController
from cbpi.controller.system_controller import SystemController
from cbpi.controller.satellite_controller import SatelliteController
from cbpi.controller.log_file_controller import LogController
@ -102,7 +107,10 @@ class CraftBeerPi:
self.step : StepController = StepController(self)
self.recipe : RecipeController = RecipeController(self)
self.notification : NotificationController = NotificationController(self)
#self.satellite: SatelliteController = SatelliteController(self)
self.satellite = None
if self.static_config.get("mqtt", False) is True:
self.satellite: SatelliteController = SatelliteController(self)
self.dashboard = DashboardController(self)
self.http_step = StepHttpEndpoints(self)
self.http_recipe = RecipeHttpEndpoints(self)
@ -212,9 +220,11 @@ class CraftBeerPi:
def notify(self, title: str, message: str, type: str = "info", action=[]) -> None:
def notify(self, title: str, message: str, type: NotificationType = NotificationType.INFO, action=[]) -> None:
self.notification.notify(title, message, type, action)
def push_update(self, topic, data, retain=False) -> None:
asyncio.create_task(self.satellite.publish(topic=topic, message=json.dumps(data), retain=retain))
async def call_initializer(self, app):
self.initializer = sorted(self.initializer, key=lambda k: k['order'])
@ -249,6 +259,8 @@ class CraftBeerPi:
await self.job.init()
await self.config.init()
if self.satellite is not None:
await self.satellite.init()
self._setup_http_index()
self.plugin.load_plugins()
self.plugin.load_plugins_from_evn()
@ -259,7 +271,8 @@ class CraftBeerPi:
await self.kettle.init()
await self.call_initializer(self.app)
await self.dashboard.init()
#await self.satellite.init()
self._swagger_setup()
return self.app

View file

@ -2,7 +2,7 @@ from socket import timeout
from typing import KeysView
from voluptuous.schema_builder import message
from cbpi.api.dataclasses import NotificationAction
from cbpi.api.dataclasses import NotificationAction, NotificationType
import logging
from unittest.mock import MagicMock, patch
from datetime import datetime
@ -17,10 +17,9 @@ class DummyActor(CBPiActor):
def __init__(self, cbpi, id, props):
super().__init__(cbpi, id, props)
async def yes(self, **kwargs):
print("YES!")
await self.cbpi.step.next()
@action("SAY HELLO", {})
async def helloWorld(self, **kwargs):
self.cbpi.notify("HELLO", "WOOHO", NotificationType.ERROR)
async def start(self):
await super().start()

View file

@ -38,7 +38,7 @@ class MashStep(CBPiStep):
self.timer = Timer(int(self.props.Timer) *60 ,on_update=self.on_timer_update, on_done=self.on_timer_done)
async def run(self):
while self.running == True:
while True:
await asyncio.sleep(1)
sensor_value = self.get_sensor_value(self.props.Sensor)
if sensor_value.get("value") >= int(self.props.Temp) and self.timer.is_running is not True:

View file

@ -1,42 +0,0 @@
import json
from cbpi.utils.encoder import ComplexEncoder
from hbmqtt.mqtt.constants import QOS_0
from hbmqtt.client import MQTTClient
from hbmqtt.mqtt.constants import QOS_1, QOS_2
from asyncio_mqtt import Client, MqttError, Will
import asyncio
class CBPiMqttClient:
def __init__(self, cbpi):
self.cbpi = cbpi
self.cbpi.bus.register("#", self.listen)
self.client = None
self._loop = asyncio.get_event_loop()
self._loop.create_task(self.init_client(self.cbpi))
async def init_client(self, cbpi):
async with Client("localhost", will=Will(topic="cbpi/diconnect", payload="MY CLIENT"))as client:
async with client.filtered_messages("cbpi/#") as messages:
await client.subscribe("cbpi/#")
async for message in messages:
await self.cbpi.actor.on("YwGzXvWMpmbLb6XobesL8n")
async def listen(self, topic, **kwargs):
if self.client is not None:
await self.client.publish(topic, str.encode(json.dumps(kwargs, cls=ComplexEncoder)), QOS_0)
def setup(cbpi):
'''
This method is called by the server during startup
Here you need to register your plugins at the server
:param cbpi: the cbpi core
:return:
'''
client = CBPiMqttClient(cbpi)

View file

@ -1,3 +0,0 @@
name: MQTT
version: 4
active: false

View file

@ -0,0 +1,49 @@
# -*- coding: utf-8 -*-
import asyncio
import random
import re
from aiohttp import web
from cbpi.api import *
@parameters([Property.Text(label="Topic", configurable=True)])
class MQTTSensor(CBPiSensor):
async def on_message(self, message):
try:
self.value = message
self.log_data(self.value)
self.push_update(message)
except Exception as e:
print(e)
def __init__(self, cbpi, id, props):
super(MQTTSensor, self).__init__(cbpi, id, props)
self.mqtt_task = self.cbpi.satellite.subcribe(self.props.Topic, self.on_message)
async def run(self):
while self.running == True:
await asyncio.sleep(1)
def get_state(self):
return dict(value=self.value)
async def on_stop(self):
if self.mqtt_task.done() is False:
self.mqtt_task.cancel()
try:
await self.mqtt_task
except asyncio.CancelledError:
pass
def setup(cbpi):
'''
This method is called by the server during startup
Here you need to register your plugins at the server
:param cbpi: the cbpi core
:return:
'''
if cbpi.static_config.get("mqtt", False) is True:
cbpi.plugin.register("MQTTSensor", MQTTSensor)

View file

@ -0,0 +1,3 @@
name: DummySensor
version: 4
active: true

View file

@ -69,6 +69,7 @@ class DashBoardHttpEndpoints:
data = await request.json()
dashboard_id = int(request.match_info['id'])
await self.cbpi.dashboard.add_content(dashboard_id, data)
print("##### SAVE")
return web.Response(status=204)
@request_mapping(path="/{id:\d+}/content", method="DELETE", auth_required=False)

11
venv3/bin/autopep8 Executable file
View file

@ -0,0 +1,11 @@
#!/Users/manuelfritsch/Documents/git/cbpi4/venv3/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from autopep8 import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
sys.exit(main())

11
venv3/bin/pycodestyle Executable file
View file

@ -0,0 +1,11 @@
#!/Users/manuelfritsch/Documents/git/cbpi4/venv3/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from pycodestyle import _main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
sys.exit(_main())

View file

@ -0,0 +1,48 @@
Main contributors
-----------------
- Hideo Hattori (https://github.com/hhatto)
- Steven Myint (https://github.com/myint)
- Bill Wendling (https://github.com/gwelymernans)
Patches
-------
- Fraser Tweedale (https://github.com/frasertweedale)
- clach04 (https://github.com/clach04)
- Marc Abramowitz (https://github.com/msabramo)
- dellis23 (https://github.com/dellis23)
- Sam Vilain (https://github.com/samv)
- Florent Xicluna (https://github.com/florentx)
- Andras Tim (https://github.com/andras-tim)
- tomscytale (https://github.com/tomscytale)
- Filip Noetzel (https://github.com/peritus)
- Erik Bray (https://github.com/iguananaut)
- Christopher Medrela (https://github.com/chrismedrela)
- 小明 (https://github.com/dongweiming)
- Andy Hayden (https://github.com/hayd)
- Fabio Zadrozny (https://github.com/fabioz)
- Alex Chernetz (https://github.com/achernet)
- Marc Schlaich (https://github.com/schlamar)
- E. M. Bray (https://github.com/embray)
- Thomas Hisch (https://github.com/thisch)
- Florian Best (https://github.com/spaceone)
- Ian Clark (https://github.com/evenicoulddoit)
- Khairi Hafsham (https://github.com/khairihafsham)
- Neil Halelamien (https://github.com/neilsh)
- Hashem Nasarat (https://github.com/Hnasar)
- Hugo van Kemenade (https://github.com/hugovk)
- gmbnomis (https://github.com/gmbnomis)
- Samuel Lelièvre (https://github.com/slel)
- bigredengineer (https://github.com/bigredengineer)
- Kai Chen (https://github.com/kx-chen)
- Anthony Sottile (https://github.com/asottile)
- 秋葉 (https://github.com/Hanaasagi)
- Christian Clauss (https://github.com/cclauss)
- tobixx (https://github.com/tobixx)
- bigredengineer (https://github.com/bigredengineer)
- Bastien Gérard (https://github.com/bagerard)
- nicolasbonifas (https://github.com/nicolasbonifas)
- Andrii Yurchuk (https://github.com/Ch00k)
- José M. Guisado (https://github.com/pvxe)
- Dai Truong (https://github.com/NovaDev94)
- jnozsc (https://github.com/jnozsc)
- Edwin Shepherd (https://github.com/shardros)

View file

@ -0,0 +1,23 @@
Copyright (C) 2010-2011 Hideo Hattori
Copyright (C) 2011-2013 Hideo Hattori, Steven Myint
Copyright (C) 2013-2016 Hideo Hattori, Steven Myint, Bill Wendling
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,453 @@
Metadata-Version: 2.1
Name: autopep8
Version: 1.5.5
Summary: A tool that automatically formats Python code to conform to the PEP 8 style guide
Home-page: https://github.com/hhatto/autopep8
Author: Hideo Hattori
Author-email: hhatto.jp@gmail.com
License: Expat License
Keywords: automation,pep8,format,pycodestyle
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Dist: pycodestyle (>=2.6.0)
Requires-Dist: toml
========
autopep8
========
.. image:: https://img.shields.io/pypi/v/autopep8.svg
:target: https://pypi.org/project/autopep8/
:alt: PyPI Version
.. image:: https://github.com/hhatto/autopep8/workflows/Python%20package/badge.svg
:target: https://github.com/hhatto/autopep8/actions
:alt: Build status
.. image:: https://codecov.io/gh/hhatto/autopep8/branch/master/graph/badge.svg
:target: https://codecov.io/gh/hhatto/autopep8
:alt: Code Coverage
autopep8 automatically formats Python code to conform to the `PEP 8`_ style
guide. It uses the pycodestyle_ utility to determine what parts of the code
needs to be formatted. autopep8 is capable of fixing most of the formatting
issues_ that can be reported by pycodestyle.
.. _PEP 8: https://www.python.org/dev/peps/pep-0008/
.. _issues: https://pycodestyle.readthedocs.org/en/latest/intro.html#error-codes
.. contents::
Installation
============
From pip::
$ pip install --upgrade autopep8
Consider using the ``--user`` option_.
.. _option: https://pip.pypa.io/en/latest/user_guide/#user-installs
Requirements
============
autopep8 requires pycodestyle_.
.. _pycodestyle: https://github.com/PyCQA/pycodestyle
Usage
=====
To modify a file in place (with aggressive level 2)::
$ autopep8 --in-place --aggressive --aggressive <filename>
Before running autopep8.
.. code-block:: python
import math, sys;
def example1():
####This is a long comment. This should be wrapped to fit within 72 characters.
some_tuple=( 1,2, 3,'a' );
some_variable={'long':'Long code lines should be wrapped within 79 characters.',
'other':[math.pi, 100,200,300,9876543210,'This is a long string that goes on'],
'more':{'inner':'This whole logical line should be wrapped.',some_tuple:[1,
20,300,40000,500000000,60000000000000000]}}
return (some_tuple, some_variable)
def example2(): return {'has_key() is deprecated':True}.has_key({'f':2}.has_key(''));
class Example3( object ):
def __init__ ( self, bar ):
#Comments should have a space after the hash.
if bar : bar+=1; bar=bar* bar ; return bar
else:
some_string = """
Indentation in multiline strings should not be touched.
Only actual code should be reindented.
"""
return (sys.path, some_string)
After running autopep8.
.. code-block:: python
import math
import sys
def example1():
# This is a long comment. This should be wrapped to fit within 72
# characters.
some_tuple = (1, 2, 3, 'a')
some_variable = {
'long': 'Long code lines should be wrapped within 79 characters.',
'other': [
math.pi,
100,
200,
300,
9876543210,
'This is a long string that goes on'],
'more': {
'inner': 'This whole logical line should be wrapped.',
some_tuple: [
1,
20,
300,
40000,
500000000,
60000000000000000]}}
return (some_tuple, some_variable)
def example2(): return ('' in {'f': 2}) in {'has_key() is deprecated': True}
class Example3(object):
def __init__(self, bar):
# Comments should have a space after the hash.
if bar:
bar += 1
bar = bar * bar
return bar
else:
some_string = """
Indentation in multiline strings should not be touched.
Only actual code should be reindented.
"""
return (sys.path, some_string)
Options::
usage: autopep8 [-h] [--version] [-v] [-d] [-i] [--global-config filename]
[--ignore-local-config] [-r] [-j n] [-p n] [-a]
[--experimental] [--exclude globs] [--list-fixes]
[--ignore errors] [--select errors] [--max-line-length n]
[--line-range line line] [--hang-closing] [--exit-code]
[files [files ...]]
Automatically formats Python code to conform to the PEP 8 style guide.
positional arguments:
files files to format or '-' for standard in
optional arguments:
-h, --help show this help message and exit
--version show program's version number and exit
-v, --verbose print verbose messages; multiple -v result in more
verbose messages
-d, --diff print the diff for the fixed source
-i, --in-place make changes to files in place
--global-config filename
path to a global pep8 config file; if this file does
not exist then this is ignored (default:
~/.config/pep8)
--ignore-local-config
don't look for and apply local config files; if not
passed, defaults are updated with any config files in
the project's root directory
-r, --recursive run recursively over directories; must be used with
--in-place or --diff
-j n, --jobs n number of parallel jobs; match CPU count if value is
less than 1
-p n, --pep8-passes n
maximum number of additional pep8 passes (default:
infinite)
-a, --aggressive enable non-whitespace changes; multiple -a result in
more aggressive changes
--experimental enable experimental fixes
--exclude globs exclude file/directory names that match these comma-
separated globs
--list-fixes list codes for fixes; used by --ignore and --select
--ignore errors do not fix these errors/warnings (default:
E226,E24,W50,W690)
--select errors fix only these errors/warnings (e.g. E4,W)
--max-line-length n set maximum allowed line length (default: 79)
--line-range line line, --range line line
only fix errors found within this inclusive range of
line numbers (e.g. 1 99); line numbers are indexed at
1
--hang-closing hang-closing option passed to pycodestyle
--exit-code change to behavior of exit code. default behavior of
return value, 0 is no differences, 1 is error exit.
return 2 when add this option. 2 is exists
differences.
Features
========
autopep8 fixes the following issues_ reported by pycodestyle_::
E101 - Reindent all lines.
E11 - Fix indentation.
E121 - Fix indentation to be a multiple of four.
E122 - Add absent indentation for hanging indentation.
E123 - Align closing bracket to match opening bracket.
E124 - Align closing bracket to match visual indentation.
E125 - Indent to distinguish line from next logical line.
E126 - Fix over-indented hanging indentation.
E127 - Fix visual indentation.
E128 - Fix visual indentation.
E129 - Fix visual indentation.
E131 - Fix hanging indent for unaligned continuation line.
E133 - Fix missing indentation for closing bracket.
E20 - Remove extraneous whitespace.
E211 - Remove extraneous whitespace.
E22 - Fix extraneous whitespace around keywords.
E224 - Remove extraneous whitespace around operator.
E225 - Fix missing whitespace around operator.
E226 - Fix missing whitespace around arithmetic operator.
E227 - Fix missing whitespace around bitwise/shift operator.
E228 - Fix missing whitespace around modulo operator.
E231 - Add missing whitespace.
E241 - Fix extraneous whitespace around keywords.
E242 - Remove extraneous whitespace around operator.
E251 - Remove whitespace around parameter '=' sign.
E252 - Missing whitespace around parameter equals.
E26 - Fix spacing after comment hash for inline comments.
E265 - Fix spacing after comment hash for block comments.
E266 - Fix too many leading '#' for block comments.
E27 - Fix extraneous whitespace around keywords.
E301 - Add missing blank line.
E302 - Add missing 2 blank lines.
E303 - Remove extra blank lines.
E304 - Remove blank line following function decorator.
E305 - Expected 2 blank lines after end of function or class.
E306 - Expected 1 blank line before a nested definition.
E401 - Put imports on separate lines.
E402 - Fix module level import not at top of file
E501 - Try to make lines fit within --max-line-length characters.
E502 - Remove extraneous escape of newline.
E701 - Put colon-separated compound statement on separate lines.
E70 - Put semicolon-separated compound statement on separate lines.
E711 - Fix comparison with None.
E712 - Fix comparison with boolean.
E713 - Use 'not in' for test for membership.
E714 - Use 'is not' test for object identity.
E721 - Use "isinstance()" instead of comparing types directly.
E722 - Fix bare except.
E731 - Use a def when use do not assign a lambda expression.
W291 - Remove trailing whitespace.
W292 - Add a single newline at the end of the file.
W293 - Remove trailing whitespace on blank line.
W391 - Remove trailing blank lines.
W503 - Fix line break before binary operator.
W504 - Fix line break after binary operator.
W601 - Use "in" rather than "has_key()".
W602 - Fix deprecated form of raising exception.
W603 - Use "!=" instead of "<>"
W604 - Use "repr()" instead of backticks.
W605 - Fix invalid escape sequence 'x'.
W690 - Fix various deprecated code (via lib2to3).
autopep8 also fixes some issues not found by pycodestyle_.
- Correct deprecated or non-idiomatic Python code (via ``lib2to3``). Use this
for making Python 2.7 code more compatible with Python 3. (This is triggered
if ``W690`` is enabled.)
- Normalize files with mixed line endings.
- Put a blank line between a class docstring and its first method
declaration. (Enabled with ``E301``.)
- Remove blank lines between a function declaration and its docstring. (Enabled
with ``E303``.)
autopep8 avoids fixing some issues found by pycodestyle_.
- ``E112``/``E113`` for non comments are reports of bad indentation that break
syntax rules. These should not be modified at all.
- ``E265``, which refers to spacing after comment hash, is ignored if the
comment looks like code. autopep8 avoids modifying these since they are not
real comments. If you really want to get rid of the pycodestyle_ warning,
consider just removing the commented-out code. (This can be automated via
eradicate_.)
.. _eradicate: https://github.com/myint/eradicate
More advanced usage
===================
By default autopep8 only makes whitespace changes. Thus, by default, it does
not fix ``E711`` and ``E712``. (Changing ``x == None`` to ``x is None`` may
change the meaning of the program if ``x`` has its ``__eq__`` method
overridden.) Nor does it correct deprecated code ``W6``. To enable these
more aggressive fixes, use the ``--aggressive`` option::
$ autopep8 --aggressive <filename>
Use multiple ``--aggressive`` to increase the aggressiveness level. For
example, ``E712`` requires aggressiveness level 2 (since ``x == True`` could be
changed to either ``x`` or ``x is True``, but autopep8 chooses the former).
``--aggressive`` will also shorten lines more aggressively. It will also remove
trailing whitespace more aggressively. (Usually, we don't touch trailing
whitespace in docstrings and other multiline strings. And to do even more
aggressive changes to docstrings, use docformatter_.)
.. _docformatter: https://github.com/myint/docformatter
To enable only a subset of the fixes, use the ``--select`` option. For example,
to fix various types of indentation issues::
$ autopep8 --select=E1,W1 <filename>
Similarly, to just fix deprecated code::
$ autopep8 --aggressive --select=W6 <filename>
The above is useful when trying to port a single code base to work with both
Python 2 and Python 3 at the same time.
If the file being fixed is large, you may want to enable verbose progress
messages::
$ autopep8 -v <filename>
Passing in ``--experimental`` enables the following functionality:
- Shortens code lines by taking its length into account
::
$ autopep8 --experimental <filename>
Use as a module
===============
The simplest way of using autopep8 as a module is via the ``fix_code()``
function:
>>> import autopep8
>>> autopep8.fix_code('x= 123\n')
'x = 123\n'
Or with options:
>>> import autopep8
>>> autopep8.fix_code('x.has_key(y)\n',
... options={'aggressive': 1})
'y in x\n'
>>> autopep8.fix_code('print( 123 )\n',
... options={'ignore': ['E']})
'print( 123 )\n'
Configuration
=============
By default, if ``$HOME/.config/pycodestyle`` (``~\.pycodestyle`` in Windows
environment) exists, it will be used as global configuration file.
Alternatively, you can specify the global configuration file with the
``--global-config`` option.
Also, if ``setup.cfg``, ``tox.ini``, ``.pep8`` and ``.flake8`` files exist
in the directory where the target file exists, it will be used as the
configuration file.
``pep8``, ``pycodestyle``, and ``flake8`` can be used as a section.
configuration file example::
[pycodestyle]
max_line_length = 120
ignore = E501
pyproject.toml
--------------
autopep8 can also use ``pyproject.toml``.
section must use ``[tool.autopep8]``, and ``pyproject.toml`` takes precedence
over any other configuration files.
configuration file example::
[tool.autopep8]
max_line_length = 120
ignore = "E501,W6" # or ["E501", "W6"]
Testing
=======
Test cases are in ``test/test_autopep8.py``. They can be run directly via
``python test/test_autopep8.py`` or via tox_. The latter is useful for
testing against multiple Python interpreters. (We currently test against
CPython versions 2.7, 3.6 3.7 and 3.8. We also test against PyPy.)
.. _`tox`: https://pypi.org/project/tox/
Broad spectrum testing is available via ``test/acid.py``. This script runs
autopep8 against Python code and checks for correctness and completeness of the
code fixes. It can check that the bytecode remains identical.
``test/acid_pypi.py`` makes use of ``acid.py`` to test against the latest
released packages on PyPI.
Troubleshooting
===============
``pkg_resources.DistributionNotFound``
--------------------------------------
If you are using an ancient version of ``setuptools``, you might encounter
``pkg_resources.DistributionNotFound`` when trying to run ``autopep8``. Try
upgrading ``setuptools`` to workaround this ``setuptools`` problem::
$ pip install --upgrade setuptools
Use ``sudo`` if you are installing to the system.
Links
=====
* PyPI_
* GitHub_
* `Travis CI`_
* Coveralls_
.. _PyPI: https://pypi.org/project/autopep8/
.. _GitHub: https://github.com/hhatto/autopep8
.. _`Travis CI`: https://travis-ci.org/hhatto/autopep8
.. _`Coveralls`: https://coveralls.io/r/hhatto/autopep8

View file

@ -0,0 +1,11 @@
autopep8.py,sha256=xHkIfzd5IPgtTeyNq19o2SLHF28bregdzXEcx3G7ucQ,151970
autopep8-1.5.5.dist-info/AUTHORS.rst,sha256=tiTPsbzGl9dtXCMEWXbWSV1zan1M-BoWtiixs46GIWk,2003
autopep8-1.5.5.dist-info/LICENSE,sha256=jR0COOSFQ0QZFMqwdB1N4-Bwobg2f3h69fIJr7YLCWo,1181
autopep8-1.5.5.dist-info/METADATA,sha256=3IbGpS9FlKP4rzVE6EXbN-0O4D2AZoZcgaXEyYpAq3c,16661
autopep8-1.5.5.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110
autopep8-1.5.5.dist-info/entry_points.txt,sha256=iHNa5_cSXw2ablVbRmfiFGMG1CNrpEPRCEjn3nspaJ8,44
autopep8-1.5.5.dist-info/top_level.txt,sha256=s2x-di3QBwGxr7kd5xErt2pom8dsFRdINbmwsOEgLfU,9
autopep8-1.5.5.dist-info/RECORD,,
../../../bin/autopep8,sha256=nvzzNDnv7luxQekE2qaJPtL_f3kgvxUAJNKivQVar48,253
autopep8-1.5.5.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
__pycache__/autopep8.cpython-37.pyc,,

View file

@ -0,0 +1,6 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.34.2)
Root-Is-Purelib: true
Tag: py2-none-any
Tag: py3-none-any

View file

@ -0,0 +1,3 @@
[console_scripts]
autopep8 = autopep8:main

View file

@ -0,0 +1 @@
autopep8

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,25 @@
Copyright © 2006-2009 Johann C. Rocholl <johann@rocholl.net>
Copyright © 2009-2014 Florent Xicluna <florent.xicluna@gmail.com>
Copyright © 2014-2020 Ian Lee <IanLee1521@gmail.com>
Licensed under the terms of the Expat License
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation files
(the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,11 @@
pycodestyle.py,sha256=xYcAkNSMHMAlz6cmdkfhwK7QC5RsRwK2pfG2_uPI2xM,103376
pycodestyle-2.6.0.dist-info/LICENSE,sha256=93IpXoGvNHjTTojlLQdiACMOx91qOeEjvFyzWqZqva4,1254
pycodestyle-2.6.0.dist-info/METADATA,sha256=WI4-bMnR66kT7MKGLVFW7xqmuotPGP0uLJProv7nhD4,30287
pycodestyle-2.6.0.dist-info/WHEEL,sha256=kGT74LWyRUZrL4VgLh6_g12IeVl_9u9ZVhadrgXZUEY,110
pycodestyle-2.6.0.dist-info/entry_points.txt,sha256=6JU_7SAppC93MBSQi1_QxDwEQUyg6cgK71ab9q_Hxco,51
pycodestyle-2.6.0.dist-info/namespace_packages.txt,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
pycodestyle-2.6.0.dist-info/top_level.txt,sha256=rHbIEiXmvsJ016mFcLVcF_d-dKgP3VdfOB6CWbivZug,12
pycodestyle-2.6.0.dist-info/RECORD,,
../../../bin/pycodestyle,sha256=pIp6u2iVgkZ61DS1jKVvK_T_RtnyEky3j3Hlf_fhhN8,258
pycodestyle-2.6.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
__pycache__/pycodestyle.cpython-37.pyc,,

View file

@ -0,0 +1,6 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.34.2)
Root-Is-Purelib: true
Tag: py2-none-any
Tag: py3-none-any

View file

@ -0,0 +1,3 @@
[console_scripts]
pycodestyle = pycodestyle:_main

View file

@ -0,0 +1 @@
pycodestyle

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1 @@
pip

View file

@ -0,0 +1,27 @@
The MIT License
Copyright 2013-2019 William Pearson
Copyright 2015-2016 Julien Enselme
Copyright 2016 Google Inc.
Copyright 2017 Samuel Vasko
Copyright 2017 Nate Prewitt
Copyright 2017 Jack Evans
Copyright 2019 Filippo Broggini
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View file

@ -0,0 +1,255 @@
Metadata-Version: 2.1
Name: toml
Version: 0.10.2
Summary: Python Library for Tom's Obvious, Minimal Language
Home-page: https://github.com/uiri/toml
Author: William Pearson
Author-email: uiri@xqz.ca
License: MIT
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Requires-Python: >=2.6, !=3.0.*, !=3.1.*, !=3.2.*
****
TOML
****
.. image:: https://img.shields.io/pypi/v/toml
:target: https://pypi.org/project/toml/
.. image:: https://travis-ci.org/uiri/toml.svg?branch=master
:target: https://travis-ci.org/uiri/toml
.. image:: https://img.shields.io/pypi/pyversions/toml.svg
:target: https://pypi.org/project/toml/
A Python library for parsing and creating `TOML <https://en.wikipedia.org/wiki/TOML>`_.
The module passes `the TOML test suite <https://github.com/BurntSushi/toml-test>`_.
See also:
* `The TOML Standard <https://github.com/toml-lang/toml>`_
* `The currently supported TOML specification <https://github.com/toml-lang/toml/blob/v0.5.0/README.md>`_
Installation
============
To install the latest release on `PyPI <https://pypi.org/project/toml/>`_,
simply run:
::
pip install toml
Or to install the latest development version, run:
::
git clone https://github.com/uiri/toml.git
cd toml
python setup.py install
Quick Tutorial
==============
*toml.loads* takes in a string containing standard TOML-formatted data and
returns a dictionary containing the parsed data.
.. code:: pycon
>>> import toml
>>> toml_string = """
... # This is a TOML document.
...
... title = "TOML Example"
...
... [owner]
... name = "Tom Preston-Werner"
... dob = 1979-05-27T07:32:00-08:00 # First class dates
...
... [database]
... server = "192.168.1.1"
... ports = [ 8001, 8001, 8002 ]
... connection_max = 5000
... enabled = true
...
... [servers]
...
... # Indentation (tabs and/or spaces) is allowed but not required
... [servers.alpha]
... ip = "10.0.0.1"
... dc = "eqdc10"
...
... [servers.beta]
... ip = "10.0.0.2"
... dc = "eqdc10"
...
... [clients]
... data = [ ["gamma", "delta"], [1, 2] ]
...
... # Line breaks are OK when inside arrays
... hosts = [
... "alpha",
... "omega"
... ]
... """
>>> parsed_toml = toml.loads(toml_string)
*toml.dumps* takes a dictionary and returns a string containing the
corresponding TOML-formatted data.
.. code:: pycon
>>> new_toml_string = toml.dumps(parsed_toml)
>>> print(new_toml_string)
title = "TOML Example"
[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00Z
[database]
server = "192.168.1.1"
ports = [ 8001, 8001, 8002,]
connection_max = 5000
enabled = true
[clients]
data = [ [ "gamma", "delta",], [ 1, 2,],]
hosts = [ "alpha", "omega",]
[servers.alpha]
ip = "10.0.0.1"
dc = "eqdc10"
[servers.beta]
ip = "10.0.0.2"
dc = "eqdc10"
*toml.dump* takes a dictionary and a file descriptor and returns a string containing the
corresponding TOML-formatted data.
.. code:: pycon
>>> with open('new_toml_file.toml', 'w') as f:
... new_toml_string = toml.dump(parsed_toml, f)
>>> print(new_toml_string)
title = "TOML Example"
[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00Z
[database]
server = "192.168.1.1"
ports = [ 8001, 8001, 8002,]
connection_max = 5000
enabled = true
[clients]
data = [ [ "gamma", "delta",], [ 1, 2,],]
hosts = [ "alpha", "omega",]
[servers.alpha]
ip = "10.0.0.1"
dc = "eqdc10"
[servers.beta]
ip = "10.0.0.2"
dc = "eqdc10"
For more functions, view the API Reference below.
Note
----
For Numpy users, by default the data types ``np.floatX`` will not be translated to floats by toml, but will instead be encoded as strings. To get around this, specify the ``TomlNumpyEncoder`` when saving your data.
.. code:: pycon
>>> import toml
>>> import numpy as np
>>> a = np.arange(0, 10, dtype=np.double)
>>> output = {'a': a}
>>> toml.dumps(output)
'a = [ "0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "6.0", "7.0", "8.0", "9.0",]\n'
>>> toml.dumps(output, encoder=toml.TomlNumpyEncoder())
'a = [ 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0,]\n'
API Reference
=============
``toml.load(f, _dict=dict)``
Parse a file or a list of files as TOML and return a dictionary.
:Args:
* ``f``: A path to a file, list of filepaths (to be read into single
object) or a file descriptor
* ``_dict``: The class of the dictionary object to be returned
:Returns:
A dictionary (or object ``_dict``) containing parsed TOML data
:Raises:
* ``TypeError``: When ``f`` is an invalid type or is a list containing
invalid types
* ``TomlDecodeError``: When an error occurs while decoding the file(s)
``toml.loads(s, _dict=dict)``
Parse a TOML-formatted string to a dictionary.
:Args:
* ``s``: The TOML-formatted string to be parsed
* ``_dict``: Specifies the class of the returned toml dictionary
:Returns:
A dictionary (or object ``_dict``) containing parsed TOML data
:Raises:
* ``TypeError``: When a non-string object is passed
* ``TomlDecodeError``: When an error occurs while decoding the
TOML-formatted string
``toml.dump(o, f, encoder=None)``
Write a dictionary to a file containing TOML-formatted data
:Args:
* ``o``: An object to be converted into TOML
* ``f``: A File descriptor where the TOML-formatted output should be stored
* ``encoder``: An instance of ``TomlEncoder`` (or subclass) for encoding the object. If ``None``, will default to ``TomlEncoder``
:Returns:
A string containing the TOML-formatted data corresponding to object ``o``
:Raises:
* ``TypeError``: When anything other than file descriptor is passed
``toml.dumps(o, encoder=None)``
Create a TOML-formatted string from an input object
:Args:
* ``o``: An object to be converted into TOML
* ``encoder``: An instance of ``TomlEncoder`` (or subclass) for encoding the object. If ``None``, will default to ``TomlEncoder``
:Returns:
A string containing the TOML-formatted data corresponding to object ``o``
Licensing
=========
This project is released under the terms of the MIT Open Source License. View
*LICENSE.txt* for more information.

View file

@ -0,0 +1,16 @@
toml/__init__.py,sha256=Au3kqCwKD0cjbf4yJGOpUFwpsY0WHsC1ZRGvWgIKmpc,723
toml/decoder.py,sha256=hSGTLf-2WBDZ_ddoCHWFy6N647XyMSh1o3rN2o4dEFg,38942
toml/encoder.py,sha256=XjBc8ayvvlsLyd_qDA4tMWDNmMFRS4DpwtuDSWBq7zo,9940
toml/ordered.py,sha256=mz03lZmV0bmc9lsYRIUOuj7Dsu5Ptwq-UtGVq5FdVZ4,354
toml/tz.py,sha256=-5vg8wkg_atnVi2TnEveexIVE7T_FxBVr_-2WVfO1oA,701
toml-0.10.2.dist-info/LICENSE,sha256=LZKUgj32yJNXyL5JJ_znk2HWVh5e51MtWSbmOTmqpTY,1252
toml-0.10.2.dist-info/METADATA,sha256=n_YkspvEihd_QXLIZZ50WVSFz3rZ_k7jQP-OU1WUpWY,7142
toml-0.10.2.dist-info/WHEEL,sha256=ADKeyaGyKF5DwBNE0sRE5pvW-bSkFMJfBuhzZ3rceP4,110
toml-0.10.2.dist-info/top_level.txt,sha256=2BO8ZRNnvJWgXyiQv66LBb_v87qBzcoUtEBefA75Ouk,5
toml-0.10.2.dist-info/RECORD,,
toml-0.10.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
toml/__pycache__/ordered.cpython-37.pyc,,
toml/__pycache__/tz.cpython-37.pyc,,
toml/__pycache__/decoder.cpython-37.pyc,,
toml/__pycache__/encoder.cpython-37.pyc,,
toml/__pycache__/__init__.cpython-37.pyc,,

View file

@ -0,0 +1,6 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.35.1)
Root-Is-Purelib: true
Tag: py2-none-any
Tag: py3-none-any

View file

@ -0,0 +1,25 @@
"""Python module which parses and emits TOML.
Released under the MIT license.
"""
from toml import encoder
from toml import decoder
__version__ = "0.10.2"
_spec_ = "0.5.0"
load = decoder.load
loads = decoder.loads
TomlDecoder = decoder.TomlDecoder
TomlDecodeError = decoder.TomlDecodeError
TomlPreserveCommentDecoder = decoder.TomlPreserveCommentDecoder
dump = encoder.dump
dumps = encoder.dumps
TomlEncoder = encoder.TomlEncoder
TomlArraySeparatorEncoder = encoder.TomlArraySeparatorEncoder
TomlPreserveInlineDictEncoder = encoder.TomlPreserveInlineDictEncoder
TomlNumpyEncoder = encoder.TomlNumpyEncoder
TomlPreserveCommentEncoder = encoder.TomlPreserveCommentEncoder
TomlPathlibEncoder = encoder.TomlPathlibEncoder

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,304 @@
import datetime
import re
import sys
from decimal import Decimal
from toml.decoder import InlineTableDict
if sys.version_info >= (3,):
unicode = str
def dump(o, f, encoder=None):
"""Writes out dict as toml to a file
Args:
o: Object to dump into toml
f: File descriptor where the toml should be stored
encoder: The ``TomlEncoder`` to use for constructing the output string
Returns:
String containing the toml corresponding to dictionary
Raises:
TypeError: When anything other than file descriptor is passed
"""
if not f.write:
raise TypeError("You can only dump an object to a file descriptor")
d = dumps(o, encoder=encoder)
f.write(d)
return d
def dumps(o, encoder=None):
"""Stringifies input dict as toml
Args:
o: Object to dump into toml
encoder: The ``TomlEncoder`` to use for constructing the output string
Returns:
String containing the toml corresponding to dict
Examples:
```python
>>> import toml
>>> output = {
... 'a': "I'm a string",
... 'b': ["I'm", "a", "list"],
... 'c': 2400
... }
>>> toml.dumps(output)
'a = "I\'m a string"\nb = [ "I\'m", "a", "list",]\nc = 2400\n'
```
"""
retval = ""
if encoder is None:
encoder = TomlEncoder(o.__class__)
addtoretval, sections = encoder.dump_sections(o, "")
retval += addtoretval
outer_objs = [id(o)]
while sections:
section_ids = [id(section) for section in sections.values()]
for outer_obj in outer_objs:
if outer_obj in section_ids:
raise ValueError("Circular reference detected")
outer_objs += section_ids
newsections = encoder.get_empty_table()
for section in sections:
addtoretval, addtosections = encoder.dump_sections(
sections[section], section)
if addtoretval or (not addtoretval and not addtosections):
if retval and retval[-2:] != "\n\n":
retval += "\n"
retval += "[" + section + "]\n"
if addtoretval:
retval += addtoretval
for s in addtosections:
newsections[section + "." + s] = addtosections[s]
sections = newsections
return retval
def _dump_str(v):
if sys.version_info < (3,) and hasattr(v, 'decode') and isinstance(v, str):
v = v.decode('utf-8')
v = "%r" % v
if v[0] == 'u':
v = v[1:]
singlequote = v.startswith("'")
if singlequote or v.startswith('"'):
v = v[1:-1]
if singlequote:
v = v.replace("\\'", "'")
v = v.replace('"', '\\"')
v = v.split("\\x")
while len(v) > 1:
i = -1
if not v[0]:
v = v[1:]
v[0] = v[0].replace("\\\\", "\\")
# No, I don't know why != works and == breaks
joinx = v[0][i] != "\\"
while v[0][:i] and v[0][i] == "\\":
joinx = not joinx
i -= 1
if joinx:
joiner = "x"
else:
joiner = "u00"
v = [v[0] + joiner + v[1]] + v[2:]
return unicode('"' + v[0] + '"')
def _dump_float(v):
return "{}".format(v).replace("e+0", "e+").replace("e-0", "e-")
def _dump_time(v):
utcoffset = v.utcoffset()
if utcoffset is None:
return v.isoformat()
# The TOML norm specifies that it's local time thus we drop the offset
return v.isoformat()[:-6]
class TomlEncoder(object):
def __init__(self, _dict=dict, preserve=False):
self._dict = _dict
self.preserve = preserve
self.dump_funcs = {
str: _dump_str,
unicode: _dump_str,
list: self.dump_list,
bool: lambda v: unicode(v).lower(),
int: lambda v: v,
float: _dump_float,
Decimal: _dump_float,
datetime.datetime: lambda v: v.isoformat().replace('+00:00', 'Z'),
datetime.time: _dump_time,
datetime.date: lambda v: v.isoformat()
}
def get_empty_table(self):
return self._dict()
def dump_list(self, v):
retval = "["
for u in v:
retval += " " + unicode(self.dump_value(u)) + ","
retval += "]"
return retval
def dump_inline_table(self, section):
"""Preserve inline table in its compact syntax instead of expanding
into subsection.
https://github.com/toml-lang/toml#user-content-inline-table
"""
retval = ""
if isinstance(section, dict):
val_list = []
for k, v in section.items():
val = self.dump_inline_table(v)
val_list.append(k + " = " + val)
retval += "{ " + ", ".join(val_list) + " }\n"
return retval
else:
return unicode(self.dump_value(section))
def dump_value(self, v):
# Lookup function corresponding to v's type
dump_fn = self.dump_funcs.get(type(v))
if dump_fn is None and hasattr(v, '__iter__'):
dump_fn = self.dump_funcs[list]
# Evaluate function (if it exists) else return v
return dump_fn(v) if dump_fn is not None else self.dump_funcs[str](v)
def dump_sections(self, o, sup):
retstr = ""
if sup != "" and sup[-1] != ".":
sup += '.'
retdict = self._dict()
arraystr = ""
for section in o:
section = unicode(section)
qsection = section
if not re.match(r'^[A-Za-z0-9_-]+$', section):
qsection = _dump_str(section)
if not isinstance(o[section], dict):
arrayoftables = False
if isinstance(o[section], list):
for a in o[section]:
if isinstance(a, dict):
arrayoftables = True
if arrayoftables:
for a in o[section]:
arraytabstr = "\n"
arraystr += "[[" + sup + qsection + "]]\n"
s, d = self.dump_sections(a, sup + qsection)
if s:
if s[0] == "[":
arraytabstr += s
else:
arraystr += s
while d:
newd = self._dict()
for dsec in d:
s1, d1 = self.dump_sections(d[dsec], sup +
qsection + "." +
dsec)
if s1:
arraytabstr += ("[" + sup + qsection +
"." + dsec + "]\n")
arraytabstr += s1
for s1 in d1:
newd[dsec + "." + s1] = d1[s1]
d = newd
arraystr += arraytabstr
else:
if o[section] is not None:
retstr += (qsection + " = " +
unicode(self.dump_value(o[section])) + '\n')
elif self.preserve and isinstance(o[section], InlineTableDict):
retstr += (qsection + " = " +
self.dump_inline_table(o[section]))
else:
retdict[qsection] = o[section]
retstr += arraystr
return (retstr, retdict)
class TomlPreserveInlineDictEncoder(TomlEncoder):
def __init__(self, _dict=dict):
super(TomlPreserveInlineDictEncoder, self).__init__(_dict, True)
class TomlArraySeparatorEncoder(TomlEncoder):
def __init__(self, _dict=dict, preserve=False, separator=","):
super(TomlArraySeparatorEncoder, self).__init__(_dict, preserve)
if separator.strip() == "":
separator = "," + separator
elif separator.strip(' \t\n\r,'):
raise ValueError("Invalid separator for arrays")
self.separator = separator
def dump_list(self, v):
t = []
retval = "["
for u in v:
t.append(self.dump_value(u))
while t != []:
s = []
for u in t:
if isinstance(u, list):
for r in u:
s.append(r)
else:
retval += " " + unicode(u) + self.separator
t = s
retval += "]"
return retval
class TomlNumpyEncoder(TomlEncoder):
def __init__(self, _dict=dict, preserve=False):
import numpy as np
super(TomlNumpyEncoder, self).__init__(_dict, preserve)
self.dump_funcs[np.float16] = _dump_float
self.dump_funcs[np.float32] = _dump_float
self.dump_funcs[np.float64] = _dump_float
self.dump_funcs[np.int16] = self._dump_int
self.dump_funcs[np.int32] = self._dump_int
self.dump_funcs[np.int64] = self._dump_int
def _dump_int(self, v):
return "{}".format(int(v))
class TomlPreserveCommentEncoder(TomlEncoder):
def __init__(self, _dict=dict, preserve=False):
from toml.decoder import CommentValue
super(TomlPreserveCommentEncoder, self).__init__(_dict, preserve)
self.dump_funcs[CommentValue] = lambda v: v.dump(self.dump_value)
class TomlPathlibEncoder(TomlEncoder):
def _dump_pathlib_path(self, v):
return _dump_str(str(v))
def dump_value(self, v):
if (3, 4) <= sys.version_info:
import pathlib
if isinstance(v, pathlib.PurePath):
v = str(v)
return super(TomlPathlibEncoder, self).dump_value(v)

View file

@ -0,0 +1,15 @@
from collections import OrderedDict
from toml import TomlEncoder
from toml import TomlDecoder
class TomlOrderedDecoder(TomlDecoder):
def __init__(self):
super(self.__class__, self).__init__(_dict=OrderedDict)
class TomlOrderedEncoder(TomlEncoder):
def __init__(self):
super(self.__class__, self).__init__(_dict=OrderedDict)

View file

@ -0,0 +1,24 @@
from datetime import tzinfo, timedelta
class TomlTz(tzinfo):
def __init__(self, toml_offset):
if toml_offset == "Z":
self._raw_offset = "+00:00"
else:
self._raw_offset = toml_offset
self._sign = -1 if self._raw_offset[0] == '-' else 1
self._hours = int(self._raw_offset[1:3])
self._minutes = int(self._raw_offset[4:6])
def __deepcopy__(self, memo):
return self.__class__(self._raw_offset)
def tzname(self, dt):
return "UTC" + self._raw_offset
def utcoffset(self, dt):
return self._sign * timedelta(hours=self._hours, minutes=self._minutes)
def dst(self, dt):
return timedelta(0)

View file

@ -1 +1 @@
{"last_check":"2021-03-03T22:25:46Z","pypi_version":"21.0.1"}
{"last_check":"2021-03-14T08:43:16Z","pypi_version":"21.0.1"}