{ "info": { "author": "Fernando Freire", "author_email": "fernando.freire@nike.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python :: 2", "Programming Language :: Python :: 3", "Topic :: Software Development :: Libraries", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Utilities" ], "description": "[![CircleCI](https://circleci.com/gh/Nike-Inc/signal_analog.svg?style=svg)](https://circleci.com/gh/Nike-Inc/signal_analog)\n# signal_analog\n\nA [`troposphere`](https://github.com/cloudtools/troposphere)-inspired library\nfor programmatic, declarative definition and management of SignalFx Charts,\nDashboards, and Detectors.\n\nThis library assumes a basic familiarity with resources in SignalFx. For a\ngood overview of the SignalFx API consult the [upstream documentation][sfxdocs].\n\n## TOC\n\n - [Features](#features)\n - [Installation](#installation)\n - [Usage](#usage)\n - [Building Charts](#building-charts)\n - [Building Dashboards](#building-dashboards)\n - [Updating Dashboards](#updating-dashboards)\n - [Dashboard Filters](#providing-dashboard-filters)\n - [Dashboard Event Overlays](#dashboard-event-overlays-and-selected-event-overlays)\n - [Creating Detectors](#creating-detectors)\n - [Detectors That Combine Data Streams](#detectors-that-combine-data-streams)\n - [Building Detectors from Existing Charts](#building-detectors-from-existing-charts)\n - [Using Flow and Combinator Functions In Formulas](#using-flow-and-combinator-functions-in-formulas)\n - [Building Dashboard Groups](#building-dashboard-groups)\n - [Updating Dashboard Group](#updating-dashboard-groups)\n - [Talking to the SignalFlow API Directly](#talking-to-the-signalflow-api-directly)\n - [General `Resource` Guidelines](#general-resource-guidelines)\n - [Creating a CLI for your resources](#creating-a-cli-for-your-resources)\n - [Documentation](#documentation)\n - [Example Code](#example-code)\n - [Contributing](#contributing)\n\n## Features\n\n - Provides bindings for the SignalFlow DSL\n - Provides abstractions for:\n - Charts\n - Dashboards, DashboardGroups\n - Detectors\n - A CLI builder to wrap resource definitions (useful for automation)\n\n## Installation\n\nAdd `signal_analog` to the requirements file in your project:\n\n```\n# requirements.txt\n# ... your other dependencies\nsignal_analog\n```\n\nThen run the following command to update your environment:\n\n```\npip install -r requirements.txt\n```\n\n## Usage\n\n`signal_analog` provides two kinds of abstractions, one for building resources\nin the SignalFx API and the other for describing metric timeseries through the\n[Signal Flow DSL][signalflow].\n\nThe following sections describe how to use `Resource` abstractions in\nconjunction with the [Signal Flow DSL][signalflow].\n\n### Building Charts\n\n`signal_analog` provides constructs for building charts in the\n`signal_analog.charts` module.\n\nConsult the [upstream documentation][charts] for more information Charts.\n\nLet's consider an example where we would like to build a chart to monitor\nmemory utilization for a single applicaton in a single environment.\n\nThis assumes a service reports metrics for application name as `app` and\nenvironment as `env` with memory utilization reporting via the\n`memory.utilization` metric name.\n\nIn a timeseries chart, all data displayed on the screen comes from at least one\n`data` definition in the SignalFlow language. Let's begin by defining our\ntimeseries:\n\n```python\nfrom signal_analog.flow import Data\n\nts = Data('memory.utilization')\n```\n\nIn SignalFlow parlance a timeseries is only displayed on a chart if it has been\n\"published\". All stream functions in SignalFlow have a `publish` method that\nmay be called at the _end_ of all timeseries transformations.\n\n```python\nts = Data('memory.utilization').publish()\n```\n\nAs a convenience, all transformations on stream functions return the callee,\nso in the above example `ts` remains bound to an instance of `Data`.\n\nNow, this timeseries isn't very useful by itself; if we attached this program\nto a chart we would see _all_ timeseries for _all_ [Riposte] applications\nreporting to SignalFx!\n\nWe can restrict our view of the data by adding a filter on application name:\n\n```python\nfrom signal_analog.flow import Data, Filter\n\napp_filter = Filter('app', 'foo')\n\nts = Data('memory.utilization', filter=app_filter).publish()\n```\n\nNow if we created a chart with this program we would only be looking at metrics\nthat relate to the `foo` application. Much better, but we're still\nlooking at instance of `foo` _regardless_ of the environment it\nlives in.\n\nWhat we'll want to do is combine our `app_filter` with another filter for the\nenvironment. The `signal_analog.combinators` module provides some helpful\nconstructs for achieving this goal:\n\n```python\nfrom signal_analog.combinators import And\n\nenv_filter = Filter('env', 'prod')\n\nall_filters = And(app_filter, env_filter)\n\nts = Data('memory.utilization', filter=all_filters).publish()\n```\n\nExcellent! We're now ready to create our chart.\n\nFirst, let's give our chart a name:\n\n```python\nfrom signal_analog.charts import TimeSeriesChart\n\nmemory_chart = TimeSeriesChart().with_name('Memory Used %')\n```\n\nLike it's `flow` counterparts, `charts` adhere to the builder pattern for\nconstructing objects that interact with the SignalFx API.\n\nWith our name in place, let's go ahead and add our program:\n\n```python\nmemory_chart = TimeSeriesChart().with_name('Memory Used %').with_program(ts)\n```\n\nEach Chart understands how to serialize our SignalFlow programs appropriately,\nso it is sufficient to simply pass in our reference here.\n\nFinally, let's change the plot type on our chart so that we see solid areas\ninstead of flimsy lines:\n\n```python\nfrom signal_analog.charts import PlotType\n\nmemory_chart = TimeSeriesChart()\\\n .with_name('Memory Used %')\\\n .with_program(ts)\n .with_default_plot_type(PlotType.area_chart)\n```\n\n[Terrific]; there's only a few more details before we have a complete chart.\n\nIn the following sections we'll see how we can create dashboards from\ncollections of charts.\n\n### Building Dashboards\n\n`signal_analog` provides constructs for building charts in the\n`signal_analog.dashboards` module.\n\nConsult the [upstream documentation][dashboards] for more information on the\nDashboard API.\n\nBuilding on the examples described in the previous section, we'd now like to\nbuild a dashboard containing our memory chart.\n\nWe start with the humble `Dashboard` object:\n\n```python\nfrom signal_analog.dashboards import Dashboard\n\ndash = Dashboard()\n```\n\nMany of the same methods for charts are available on dashboards as well, so\nlet's give our dashboard a memorable name and configure it's API token:\n\n```python\ndash.with_name('My Little Dashboard: Metrics are Magic')\\\n .with_api_token('my-api-token')\n```\n\nOur final task will be to add charts to our dashboard and create it in the API!\n\n```python\nresponse = dash\\\n .with_charts(memory_chart)\\\n .with_api_token('my-api-token')\\\n .create()\n```\n\nAt this point one of two things will happen:\n\n - We receive some sort of error from the SignalFx API and an exception\n is thrown\n - We successfully created the dashboard, in which case the JSON response is\n returned as a dictionary.\n\nAlso, if you have an existing Dashboard Group and you want this new dashboard to be part of that dashboard group, you\n can pass that group id of the dashboard group when creating the dashboard. Something like this:\n\n```python\nresponse = dash\\\n .with_charts(memory_chart)\\\n .with_api_token('my-api-token')\\\n .create(group_id=\"asdf;lkj\")\n``` \n\nNow, storing API keys in source isn't ideal, so if you'd like to see how you\ncan pass in your API keys at runtime check the documentation below to see how\nyou can [dynamically build a CLI for your resources](#cli-builder).\n\n### Updating Dashboards\nOnce you have created a dashboard you can update properties like name and\ndescription:\n\n```python\ndash.update(\n name='updated_dashboard_name',\n description='updated_dashboard_description'\n)\n```\n\n`Dashboard` updates will also update any `Chart` configurations it owns.\n\n Note: If the given dashboard does not already exist, `update` will create a new dashboard for you\n\n### Providing Dashboard Filters\n\nDashboards can be configured to provide various filters that affect the behavior of all configured charts (overriding any conflicting filters at the chart level). You may wish to do this in order to quickly change the environment that you're observing for a given set of charts.\n\n\n```python\nfrom signal_analog.filters import DashboardFilters, FilterVariable, FilterSource, FilterTime\napp_var = FilterVariable().with_alias('app')\\\n.with_property('app')\\\n.with_is_required(True)\\\n.with_value('foo')\n\nenv_var = FilterVariable().with_alias('env')\\\n.with_property('env')\\\n.with_is_required(True)\\\n.with_value('prod')\n\naws_src = FilterSource().with_property(\"aws_region\").with_value('us-west-2')\n\ntime = FilterTime().with_start(\"-1h\").with_end(\"Now\")\n\napp_filter = DashboardFilters() \\\n.with_variables(app_var, env_var) \\ \n.with_sources(aws_src) \\\n.with_time(time)\n```\nSo, here we are creating a few filters \"app=foo\" and \"env=prod\", \na source filter \"aws_region=us-west-2\" and\na time filter \"-1h till Now\"\nNow we can pass this config to a dashboard object:\n\n```python\nresponse = dash\\\n.with_charts(memory_chart)\\\n.with_api_token('my-api-token')\\\n.with_filters(app_filter)\\\n.create()\n```\n\nIf you are updating an existing dashboard:\n\n```python\nresponse = dash\\\n.with_filters(app_filter)\\\n.update()\n```\n\n### Dashboard Event Overlays and Selected Event Overlays\n\nTo view events overlayed on your charts within a dashboard requires an event to be viewed, a chart with showEventLines\nenabled, and a dashboard with the correct eventOverlays settings (and selectedEventOverlays to show events by default).\n\nAssuming that the events you would like to see exist; you would make a chart with showEventLines like so:\n\n```python\nfrom signal_analog.flow import Data\nfrom signal_analog.charts import TimeSeriesChart\nprogram = Data('cpu.utilization').publish()\nchart = TimeSeriesChart().with_name('Chart With Event Overlays')\\\n .with_program(program).show_event_lines(True)\n```\nWith our chart defined, we are ready to prepare our event overlays and selected event overlays for the dashboard.\nFirst we define the event signals we would like to match. In this case, we will look for an event named \"test\" (include\n leading and/or trailing asterisks as wildcards if you need partial matching).\nNext we use those event signals to create our eventOverlays, making sure to include a color index for our event's symbol,\nand setting event line to True.\nWe also pass our event signals along to the selectedEventOverlays, which will tell the dashboard to display matching\nevents by default.\n\n```python\nfrom signal_analog.eventoverlays import EventSignals, EventOverlays, SelectedEventOverlays\nevents = EventSignals().with_event_search_text(\"*test*\")\\\n .with_event_type(\"eventTimeSeries\")\n\neventoverlay = EventOverlays().with_event_signals(events)\\\n .with_event_color_index(1)\\\n .with_event_line(True)\n\nselectedeventoverlay = SelectedEventOverlays()\\\n .with_event_signals(events)\n```\n\nNext we combine our chart, our event overlay, and our selected event overlay into a dashboard object:\n\n```python\nfrom signal_analog.dashboards import Dashboard\ndashboard_with_event_overlays = Dashboard().with_name('Dashboard With Overlays')\\\n .with_charts(chart)\\\n .with_event_overlay(eventoverlay)\\\n .with_selected_event_overlay(selectedeventoverlay)\n```\n\nFinally we build our resources in SignalFX with the cli builder:\n\n```python\nif __name__ == '__main__':\n from signal_analog.cli import CliBuilder\n cli = CliBuilder().with_resources(dashboard_with_event_overlays)\\\n .build()\n cli()\n```\n\n### Creating Detectors\n\n`signal_analog` provides a means of managing the lifecycle of `Detectors` in\nthe `signal_analog.detectors` module. As of `v0.21.0` only a subset of\nthe full Detector API is supported.\n\nConsult the [upstream documentation][detectors] for more information about\nDetectors.\n\nDetectors are comprised of a few key elements:\n\n - A name\n - A SignalFlow Program\n - A set of rules for alerting\n\nWe start by building a `Detector` object and giving it a name:\n\n```python\nfrom signal_analog.detectors import Detector\n\ndetector = Detector().with_name('My Super Serious Detector')\n```\n\nWe'll now need to give it a program to alert on:\n\n```python\nfrom signal_analog.flow import Program, Detect, Filter, Data\nfrom signal_analog.combinators import GT\n\n# This program fires an alert if memory utilization is above 90% for the\n# 'bar' application.\ndata = Data('memory.utilization', filter=Filter('app', 'bar')).publish(label='A')\nalert_label = 'Memory Utilization Above 90'\ndetect = Detect(GT(data, 90)).publish(label=alert_label)\n\ndetector.with_program(Program(detect))\n```\n\nWith our name and program in hand, it's time to build up an alert rule that we\ncan use to notify our teammates:\n\n```python\n# We provide a number of notification strategies in the detectors module.\nfrom signal_analog.detectors import EmailNotification, Rule, Severity\n\ninfo_rule = Rule()\\\n # From our detector defined above.\n .for_label(alert_label)\\\n .with_severity(Severity.Info)\\\n .with_notifications(EmailNotification('me@example.com'))\n\ndetector.with_rules(info_rule)\n\n# We can now create this resource in SignalFx:\ndetector.with_api_token('foo').create()\n# For a more robust solution consult the \"Creating a CLI for your Resources\"\n# section below.\n```\n\nTo add multiple alerting rules we would need to use different `detect`\nstatements with distinct `label`s to differentiate them from one another.\n\n#### Detectors that Combine Data Streams\n\nMore complex detectors, like those created as a function of two other data\nstreams, require a more complex setup including data stream assignments.\nIf we wanted to create a detector that watched for an average above a certain\nthreshold, we may want to use the quotient of the sum() of the data and the\ncount() of the datapoints over a given period of time.\n\n```python\nfrom signal_analog.flow import \\\n Assign, \\\n Data, \\\n Detect, \\\n Ref, \\\n When\n\nfrom signal_analog.combinators import \\\n Div, \\\n GT\n\nprogram = Program( \\\n Assign('my_var', Data('cpu.utilization')) \\\n Assign('my_other_var', Data('cpu.utilization').count()) \\\n Assign('mean', Div(Ref('my_var'), Ref('my_other_var'))) \\\n Detect(When(GT(Ref('mean'), 2000))) \\\n)\n\nprint(program)\n```\n\nThe above code generates the following program:\n\n```\nmy_var = data('cpu.utilization')\nmy_other_var = data('cpu.utilization').count()\nmean = (my_var / my_other_var)\n\nwhen(detect(mean > 2000))\n```\n\n#### Building Detectors from Existing Charts\n\nWe can also build up Detectors from an existing chart, which allows us to reuse\nour SignalFlow program and ensure consistency between what we're monitoring\nand what we're alerting on.\n\nLet's assume that we already have a chart defined for our use:\n\n```python\nfrom signal_analog.flow import Program, Data\nfrom signal_analog.charts import TimeSeriesChart\n\nprogram = Program(Data('cpu.utilization').publish(label='A'))\ncpu_chart = TimeSeriesChart().with_name('Disk Utilization').with_program(program)\n```\n\nIn order to alert on this chart we'll use the `from_chart` builder for\ndetectors:\n\n```python\nfrom signal_analog.combinators import GT\nfrom signal_analog.detectors import Detector\nfrom signal_analog.flow import Detect\n\n# Alert when CPU utilization rises above 95%\ndetector = Detector()\\\n .with_name('CPU Detector')\\\n .from_chart(\n cpu_chart,\n # `p` is the Program object from the cpu_chart we passed in.\n lambda p: Detect(GT(p.find_label('A'), 95).publish(label='Info Alert'))\n )\n```\n\nThe above example won't actually alert on anything until we add a `Rule`, which\nyou can find examples for in the previous section.\n\n### Linking Charts to Existing Detectors\n\nTo see a visualization of a Detector's status from within a chart, the `signal_analog.flow` module provides an Alert data stream that can create a signal flow statement. That statement can be appended to the charts Program object. In this example we assume a Detector was previously created. To create the link we will need the detector id. One place to obtain the detector id is to navigate to the detector in the web user interface. The url will have the id in it. The url has the form: https://app.signalfx.com/#/detector/v2/{detector_id}\n\nTo refresh our memory, our data in the previous chart example was:\n\n```python\nts = Data('memory.utilization', filter=all_filters).publish()\n```\n\nWe can append an additional alert data stream. Import Program and Alerts form the `signal_analog.flow` module. First we need to wrap the Data object in a Program object:\n\n```python\nts_program = Program(ts)\n```\n\nThen we can create a new statement using an Alert object with the detector id, publish the stream, and append the new statement to our program:\n\n```python\nnotifications = Alerts(detector_id).publish()\nts_program.statements.append(notifications)\n```\n\n The program can be included in a chart as usual:\n\n ```python\n memory_chart = TimeSeriesChart()\\\n .with_program(ts_program)\n .with_default_plot_type(PlotType.area_chart)\n```\n\n By default the alert will show as a green box around the chart when the Detector is not in Alarm. The Detector can also be accessed from the bell icon in the upper right corner of the chart. \n\n### Using Flow and Combinator Functions In Formulas\n\n`signal_analog` also provides functions for combining SignalFlow statements\ninto more complex SignalFlow Formulas. These sorts of Formulas can be useful\nwhen creating more complex detectors and charts. For instance, if you would like\nto multiply one data stream by another and receive the sum of that Formula,\nit can be accomplished using Op and Mul like so:\n\n```python\nfrom signal_analog.flow import Op, Program, Data\nfrom signal_analog.combinators import Mul\n\n# Multiply stream A by stream B and sum the result\n A = Data('request.mean')\n\n B = Data('request.count')\n\n C = Op(Mul(A,B)).sum()\n```\n\nPrint(C) in the above example would produce the following output:\n\n```\n(data(\"request.mean\") * data(\"request.count\")).sum()\n```\n\n### Building Dashboard Groups\n\n`signal_analog` provides abstractions for building dashboard groups in the\n`signal_analog.dashboards` module.\n\nConsult the [upstream documentation][dashboard-groups] for more information on\nthe Dashboard Groups API.\n\nBuilding on the examples described in the previous section, we'd now like to\nbuild a dashboard group containing our dashboards.\n\nFirst, lets build a couple of Dashboard objects similar to how we did it in\nthe `Building Dashboards` example:\n\n```python\nfrom signal_analog.dashboards import Dashboard, DashboardGroup\n\ndg = DashboardGroup()\ndash1 = Dashboard().with_name('My Little Dashboard1: Metrics are Magic')\\\n .with_charts(memory_chart)\ndash2 = Dashboard().with_name('My Little Dashboard2: Metrics are Magic')\\\n .with_charts(memory_chart)\n```\n**Note: we do not create Dashboard objects ourselves, the DashboardGroup object\nis responsible for creating all child resources.**\n\nMany of the same methods for dashboards are available on dashboard groups as\nwell, so let's give our dashboard group a memorable name and configure it's\nAPI token:\n\n```python\n\ndg.with_name('My Dashboard Group')\\\n .with_api_token('my-api-token')\n```\n\nOur final task will be to add dashboard to our dashboard group and create it\nin the API!\n\n```python\nresponse = dg\\\n .with_dashboards(dash1)\\\n .with_api_token('my-api-token')\\\n .create()\n```\n\nNow, storing API keys in source isn't ideal, so if you'd like to see how you\ncan pass in your API keys at runtime check the documentation below to see how\nyou can [dynamically build a CLI for your resources](#cli-builder).\n\n### Updating Dashboard Groups\n\nOnce you have created a dashboard group, you can update properties like name\nand description of a dashboard group or add/remove dashboards in a group.\n\n*Example 1:*\n\n```python\ndg.with_api_token('my-api-token')\\\n .update(name='updated_dashboard_group_name',\n description='updated_dashboard_group_description')\n```\n\n*Example 2:*\n\n```python\ndg.with_api_token('my-api-token').with_dashboards(dash1, dash2).update()\n```\n\n### Talking to the SignalFlow API Directly\n\nIf you need to process SignalFx data outside the confince of the API it may be\nuseful to call the SignalFlow API directly. Note that you may incur time\npenalties when pulling data out depending on the source of the data\n(e.g. AWS/CloudWatch).\n\nSignalFlow constructs are contained in the `flow` module. The following is an\nexample SignalFlow program that monitors an API services (like [Riposte])\nRPS metrics for the `foo` application in the `test` environment.\n\n```python\nfrom signal_analog.flow import Data, Filter\nfrom signal_analog.combinators import And\n\nall_filters = And(Filter('env', 'prod'), Filter('app', 'foo'))\n\nprogram = Data('requests.count', filter=all_filters)).publish()\n```\n\nYou now have an object representation of the SignalFlow program. To take it for\na test ride you can use the official SignalFx client like so:\n\n```python\n# Original example found here:\n# https://github.com/signalfx/signalfx-python#executing-signalflow-computations\n\nimport signalfx\nfrom signal_analog.flow import Data, Filter\nfrom signal_analog.combinators import And\n\napp_filter = Filter('app', 'foo')\nenv_filter = Filter('env', 'prod')\nprogram = Data('requests.count', filter=And(app_filter, env_filter)).publish()\n\nwith signalfx.SignalFx().signalflow('MY_TOKEN') as flow:\n print('Executing {0} ...'.format(program))\n computation = flow.execute(str(program))\n\n for msg in computation.stream():\n if isinstance(msg, signalfx.signalflow.messages.DataMessage):\n print('{0}: {1}'.format(msg.logical_timestamp_ms, msg.data))\n if isinstance(msg, signalfx.signalflow.messages.EventMessage):\n print('{0}: {1}'.format(msg.timestamp_ms, msg.properties))\n```\n\n### General `Resource` Guidelines\n\n#### Charts Always Belong to Dashboards\n\nIt is always assumed that a Chart belongs to an existing Dashboard. This makes\nit easier for the library to manage the state of the world.\n\n#### Resource Names are Unique per Account\n\nIn a `signal_analog` world it is assumed that all resource names are unique.\nThat is, if we have two dashboards 'Foo Dashboard', when we attempt to update\n_either_ dashboard via `signal_analog` we expect to see errors.\n\nResource names are assumed to be unique in order to simplify state management\nby the library itself. In practice we have not found this to be a major\ninconvenience.\n\n#### Configuration is the Source of Truth\n\nWhen conflicts arise between the state of a resource in your configuration and\nwhat SignalFx thinks that state should be, this library **always** prefers the\nlocal configuration.\n\n#### Only \"CCRUD\" Methods Interact with the SignalFx API\n\n`Resource` objects contain a number of builder methods to enable a \"fluent\" API\nwhen describing your project's dashboards in SignalFx. It is assumed that these\nmethods do not perform state-affecting actions in the SignalFx API.\n\nOnly \"CCRUD\" (Create, Clone, Read, Update, and Delete) methods will affect the\nstate of your resources in SignalFx.\n\n### Creating a CLI for your Resources\n\n`signal_analog` provides builders for fully featured command line clients that\ncan manage the lifecycle of sets of resources.\n\n#### Simple CLI integration\n\nIntegrating with the CLI is as simple as importing the builder and passing\nit your resources. Let's consider an example where we want to update two\nexisting dashboards:\n\n```python\n#!/usr/bin/env python\n\n# ^ It's always good to include a \"hashbang\" so that your terminal knows\n# how to run your script.\n\nfrom signal_analog.dashboards import Dashboard\nfrom signal_analog.cli import CliBuilder\n\ningest_dashboard = Dashboard().with_name('my-ingest-service')\nservice_dashboard = Dashboard().with_name('my-service')\n\nif __name__ == '__main__':\n cli = CliBuilder()\\\n .with_resources(ingest_dashboard, service_dashboard)\\\n .build()\n cli()\n```\n\nAssuming we called this `dashboards.py` we could run it in one of two ways:\n\n - Give the script execution rights and run it directly\n (typically `chmod +x dashboards.py`)\n - `./dashboards.py --api-key mykey update`\n - Pass the script in to the Python executor\n - `python dashboards.py --api-key mykey update`\n\nIf you want to know about the available actions you can take with your new\nCLI you can always the `--help` command.\n\n```shell\n./dashboards.py --help\n```\n\nThis gives you the following features:\n - Consistent resource management\n - All resources passed to the CLI builder can be updated with one\n `update` invocation, rather than calling the `update()` method on each\n resource indvidually\n - API key handling for all resources\n - Rather than duplicating your API key for each resource, you can instead\n invoke the CLI with an API key\n - This also provides a way to supply keys for users who don't want to\n store them in source control (that's you! don't store your keys in\n source control)\n\n## Documentation\n\n- [Signal Analog Documentation](https://signal-analog.readthedocs.io/)\n- [Introductory Article on Medium](https://medium.com/nikeengineering/introducing-signal-analog-the-troposphere-like-library-for-automating-monitoring-resources-c99eb8c2dca7)\n\n## Example Code\n\n- See [examples](https://github.com/Nike-Inc/signal_analog/tree/master/examples) included in this project.\n\n## Contributing\n\nPlease read our [docs here for more info about contributing](CONTRIBUTING.md).\n\n[sfxdocs]: https://developers.signalfx.com/\n[signalflow]: https://developers.signalfx.com/signalflow_analytics/signalflow_overview.html\n[charts]: https://developers.signalfx.com/charts_reference.html\n[terrific]: https://media.giphy.com/media/jir4LEGA68A9y/200.gif\n[dashboards]: https://developers.signalfx.com/dashboards_reference.html\n[dashboard-groups]: https://developers.signalfx.com/dashboard_groups_reference.html\n[detectors]: https://developers.signalfx.com/detectors_reference.html\n[Riposte]: https://github.com/Nike-inc/riposte\n\n\n## 2.9.3 (2020-3-18)\n\n* Added `Alerts` in `signal_analog.flow` module to allow linking Detectors to Charts. \n\n## 2.9.2 (2019-11-11)\n\n* `StrArg` will only reject `None` instead of all falsey values, allowing `0` to be given as a value.\n\n## 2.9.1 (2019-10-30)\n\n* Resources have learned that deleting nothing results in nothing, and will\nstop complaining about this scenario (it will still register it's displeasure\nin a debug log message)\n\n## 2.9.0 (2019-09-26)\n\n* Added `BigPandaNotification` for BigPanda integration within detectors.\n\n## 2.8.0 (2019-09-06)\n\n* Added `groupBy` support to the chart options. Allows grouping for example of HeatMap charts into groups on multiple levels.\n* Added support for `colorScale2` option on HeatMap charts. This allows to set custom chart colors for a defined range of values.\n\n## 2.7.2 (2019-05-14)\n\n### Fixed\n\n - `signal_analog` has learned to use the `groupId` field when updating\n Dashboard resources after the recent Sfx API changes\n\n### Updated\n\n - As many documentation links as possible since the last doc update from Sfx.\n Notable missing updates are those for 3rd party integration Notifications in\n the `signal_analog.detectors` module.\n\n## 2.7.0 (2019-04-03)\n\n### Updated\n * Removed dashboard numbering for two reasons:\n 1. There was a bug in the logic that caused dashboards to be deleted and recreated on update.\n 1. The functionality is no longer needed as SignalFx automatically maintains the order that dashboards were provided and allows easy reordering in the UI.\n * AxisOptions are now optional where used\n\n### Fixed\n\n* Fixing applyIfExists option for Dashboard variables.\n\n## 2.6.0 (2019-03-21)\n\n * AxisOption parameters should be optional.\n * Additional documentation.\n\n## 2.5.0 (2019-03-20)\n\n * Added `Plot` class, a helper class that gives an interface more like that found in the SignalFx UI.\n * Added `RollupType` enum for specifying the roll-up used in Charts.\n * Added additional documentation links to README.\n * Fix: TextCharts weren't working\n * Fix: YAML load deprecation warning in logging config\n\n## 2.4.0\n\n * Add numbering to dashboards in a dashboard group for better organization \n of dashboards\n\n## 2.3.2 (2018-11-12)\n\n * The `percentile` function on `signal_analog.flow.Data` objects has been\n fixed to use the correct constructor\n\n## 2.3.1 (2018-11-06)\n\n * signal-analog now prefers `simplejson` if it is available on the path,\n falling back to the `json` module otherwise.\n\n## 2.3.0 (2018-10-30)\n\n * DashboardGroup has learned how to accept SignalFX Team ids so that they can\n be associated with pre-existing teams via the `with_teams` builder method.\n\n## 2.2.2 (2018-10-03)\n\n### Fixed\n * Add `deprecation` to setup.py.\n\n## 2.2.1 (2018-10-02)\n\n### Changed\n * Added `with_secondary_visualization` function to enable display of various meters (Sparkline, Linear, Radial) in \n Single Value charts. This replaces the now defunct `with_sparkline_hidden` function. This will not be a \n 'breaking change' until version 3.0.0 when `with_sparkline_hidden` will be removed from `signal_analog`.\n\n * Added the `deprecation` Python library to this project to note when `with_sparkline_hidden` should be removed. Upon \n version matching 3.0.0 or higher the tests for that function will begin to fail notifying whoever is releasing that \n version to remove the defunct `with_sparkline_hidden` function and tests.\n\n## 2.2.0 (2018-09-27)\n\n### Changed\n * Dashboard Create method to accept group id of an existing dashboard group in which case the new dashboard will be\n part of the dashboard group provided\n\n Example:\n```python\nresponse = dashboard\\\n .with_charts(memory_chart)\\\n .with_api_token('my-api-token')\\\n .create(group_id=\"asdf;lkj\")\n```\n * Dashboard Group create method to pass group id of the newly created dashboard group to the dashboard create \n method so that we can avoid a few redundant calls like cloning and deleting the dashboards\n\n## 2.1.0 (2018-08-21)\n\n### Added\n\n * ListCharts learned how to filter legend options via the\n `with_legend_options` builder\n * Future chart types that can filter legend options may now take advantage\n of the `signal_analog.charts.LegendOptionsMixin` class\n * The `FieldOption` class has learned to accept `SignalFxFieldOption`s which\n provide mappings between field options seen in the UI and those used in the\n API\n * e.g. `Plot Name` in the UI and `sf_originatingMetric` in the API\n * A new `TextChart` object has been added to `signal_analog.charts` that\n enables text descriptions to be added to dashboards.\n * `PublishLabelOptions` has learned to accept prefix, suffix, and unit\n arguments when labelling data on charts.\n\n### Changed\n\n * `PublishLabelOptions` has learned to accept all arguments as optional\n with the exception of the `label` argument.\n\n### Fixed\n\n * A fix has been added for Python 2 users that prevented successful\n dashboard updates.\n\n## 2.0.0 (2018-07-24)\n\nFor assistance migrating from 1.x to 2.x please consult the\n[migration guide][migration-1x].\n\n### Added\n\n * Add support for the `dimensions`, `fill`, `integrate`, `kpss`,\n `rateofchange` methods\n\n\n### Removed\n\n * `map` method support has been removed\n * It didn't work properly to begin with, and will require some finagling\n to get right given our approach to building SignalFlow statements\n\n### Fixes\n\n * `top` and `bottom` method signatures have been fixed to use `count`, `by`,\n and `percentage` arguments\n * The following functions have been updated to raise an error if both\n `by` and `over` are defined in the same method call:\n * `count`, `max`, `mean`, `mean_plus_stddev`, `median`, `min`,\n `percentile`, `random`, `size`, `stddev`, `sum`, `variance`\n * `delta` has been updated to no longer accept any method arguments\n * `ewma` has been updated to support the `over` key\n\n## 1.6.0 (2018-07-18)\n\n * Add combinators for less-than-or-equal-to (`LTE`) and greater-than-or-equal-to (`GTE`)\n\n## 1.5.1 (2018-06-21)\n\n * Fix detector update logic to include all fields instead of just name/description\n\n## 1.5.0(2018-05-16)\n\n * Added `include_zero` method to `TimeSeriesChart` to allow setting the `includeZero` option.\n\n## 1.4.0(2018-05-08)\n\n * Implements functionality to add event overlays and selected (default) event overlays to dashboards \n at dashboard creation or update. Includes wildcard matching using the asterisk (*) symbol. \n\n## 1.3.0(2018-04-17)\n\n * Implementing the rest of the Dashboard Filters: `source` and `time`\n\n## 1.2.0 (2018-04-11)\n * Added an Assign function that will enable more complex detectors which are constructed by combining multiple data streams\n * Added a Ref flow operator that will enable referencing assignments in a way that can be validated at later steps by checking for an Assign object with a match between the reference string and the assignee\n\n## 1.1.0 (2018-04-04)\n * Introducing Dashboard Filters(only variables as of now) which can be configured to provide various filters that affect the behavior of all configured charts (overriding any conflicting filters at the chart level). You may wish to do this in order to quickly change the environment that you're observing for a given set of charts.\n\n## 1.0.0 (2018-04-02)\n\n * Symbolic release for `signal_analog`. Future version bumps should conform\n to the `semver` policy outlined [here][deployment].\n\n## 0.25.1 (2018-03-22)\n\n * The timeshift method's arguments changed. Now accepts a single argument for offset.\n\n## 0.24.0 (2018-03-09)\n\n * Fix string parsing to not exclude boolean False, which is required for certain functions like .publish()\n\n## 0.23.0 (2018-03-06)\n\n * Added Op class in flow.py to allow multiplying and dividing datastreams\n to create SignalFlow Functions\n\n## 0.22.0 (2018-03-01)\n\n * Added Mul and Div combinators for multiplying and dividing streams\n * Added \"enable\" option for publishing a stream. Setting enable=False\n will hide that particular stream in a chart/detector.\n\n## 0.21.0 (2018-02-28)\n\n * Dashboard Group support has been added giving you the ability group sets of\n dashboards together in a convenient construct\n * Detector support has been added giving you the ability to create detectors\n from scratch or re-use the SignalFlow program of an existing Chart\n * Dashboards and Charts now update via their `id` instead of by name to\n mitigate name conflicts when creating multiple resources with the same name\n * Dry-run results are now more consistent between all resources and expose\n the API call (sans-headers) that would have been made to use for the given\n resource\n\n## 0.20.0 (2018-01-31)\n\n * Dashboards have learned how to update their child resources (e.g. if you\n add a chart in your config, the change will be reflected when you next run\n your configuration against SignalFx)\n * The CLI builder has learned how to pass dry-run options to its configured resources\n * Minor bugfixes for the `signal_analog.flow` module\n\n## 0.19.1 (2018-01-26)\n\n * Added click to setup.py\n\n## 0.19.0 (2018-01-19)\n\n * Added CLI builder to create and update dashboard resources\n\n## 0.18.0 (2018-01-11)\n\n * Dashboard resources have learned to interactively prompt the user if the user wants to\n create a new dashboard if there is a pre-existing match (this behavior is disabled\n by default).\n * Added \"Update Dashboard\" functionality where a user can update the properties of a dashboard(only name and description for now)\n\n## 0.17.0 (2018-01-11)\n * Added Heatmap Chart style\n * Added by Jeremy Hicks\n\n## 0.16.0 (2018-01-10)\n * Added the ability to sort a list chart by value ascending/descending\n * Added by Jeremy Hicks\n\n## 0.15.0 (2018-01-08)\n\n * Added \"Scale\" to ColorBy class for coloring thresholds in SingleValueChart\n * Added by Jeremy Hicks\n\n## 0.14.0 (2018-01-04)\n\n * Added List Chart style\n * Added by Jeremy Hicks\n\n## 0.13.0 (2018-01-04)\n\n * Dashboard resources have learned how to force create themselves in the\n SignalFx API regardless of a pre-existing match (this behavior is disabled\n by default).\n\n## 0.12.0 (2017-12-21)\n\n * Dashboard resources have learned how to check for themselves in the\n SignalFx API, and will no longer create themselves if an exact match is found\n\n## 0.3.0 (2017-09-25)\n\n * Adds support for base Resource object. Will be used for Chart/Dashboard\n abstractions in future versions.\n * Adds support for base Chart and TimeSeriesChart objects. Note that some\n TimeSeriesChart builder options have not yet been implemented (and marked\n clearly with NotImplementedErrors)\n\n## 0.2.0 (2017-09-18)\n\n * Adds support for function combinators like `and`, `or`, and `not`\n\n## 0.1.1 (2017-09-14)\n\n * Add README documentation\n\n## 0.1.0 (2017-09-14)\n\n * Initial release\n\n[deployment]: https://github.com/Nike-Inc/signal_analog/wiki/Developers-::-Deployment\n[migration-1x]: ./docs/migrating_from_1.x.md\n\n\n", "description_content_type": "text/markdown", "docs_url": null, "download_url": "", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/Nike-inc/signal_analog", "keywords": "signal_analog signalfx dashboards charts detectors monitoring signalflow", "license": "BSD 3-Clause License", "maintainer": "", "maintainer_email": "", "name": "signal-analog", "package_url": "https://pypi.org/project/signal-analog/", "platform": "", "project_url": "https://pypi.org/project/signal-analog/", "project_urls": { "Homepage": "https://github.com/Nike-inc/signal_analog" }, "release_url": "https://pypi.org/project/signal-analog/2.9.3/", "requires_dist": [ "enum34", "six", "requests", "click", "email-validator", "pyyaml", "markdown", "deprecation" ], "requires_python": "", "summary": "A troposphere-like library for managing SignalFxCharts, Dashboards, and Detectors.", "version": "2.9.3", "yanked": false, "yanked_reason": null }, "last_serial": 6890889, "releases": { "0.24.0": [ { "comment_text": "", "digests": { "md5": "7303ba725cb1510309918d5ba1ae1848", "sha256": "446768ac5e125e805ae7a16750509091cc17e6f7835cad1ad13e76d3f503441d" }, "downloads": -1, "filename": "signal_analog-0.24.0.macosx-10.12-x86_64.tar.gz", "has_sig": false, "md5_digest": "7303ba725cb1510309918d5ba1ae1848", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 66280, "upload_time": "2018-04-02T20:40:47", "upload_time_iso_8601": "2018-04-02T20:40:47.888173Z", "url": "https://files.pythonhosted.org/packages/57/20/b6974a357e87c97b52739dfef0d533e4d3f5b6a9ca5b9db8e8f0e21322ed/signal_analog-0.24.0.macosx-10.12-x86_64.tar.gz", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "9a49008bf6ad7681ea78b7f6cf4ecc3b", "sha256": "bff1bdd46b4cacb75dd5a87c7811293a7f592da30754572a5d09be865fb11f57" }, "downloads": -1, "filename": "signal_analog-0.24.0-py3.6.egg", "has_sig": false, "md5_digest": "9a49008bf6ad7681ea78b7f6cf4ecc3b", "packagetype": "bdist_egg", "python_version": "3.6", "requires_python": null, "size": 75771, "upload_time": "2018-04-02T20:40:46", "upload_time_iso_8601": "2018-04-02T20:40:46.687645Z", "url": "https://files.pythonhosted.org/packages/b8/11/8672caea6bf6fa3787937b9055de25468803ff81bce379d07d07e0c09988/signal_analog-0.24.0-py3.6.egg", "yanked": false, "yanked_reason": null } ], "0.25.0": [ { "comment_text": "", "digests": { "md5": "d9ce049901c9a00e2c3aeb5aeefac5da", "sha256": "3c2532128da0f1970a11bae4c35c1f19198b7f5ad17855e9fcc579137582440c" }, "downloads": -1, "filename": "signal_analog-0.25.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "d9ce049901c9a00e2c3aeb5aeefac5da", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 43162, "upload_time": "2018-04-02T20:40:44", "upload_time_iso_8601": "2018-04-02T20:40:44.136015Z", "url": "https://files.pythonhosted.org/packages/12/1d/f24f0f090e0d50314b857a6c298a7c8a7eeea9eab07ca345ca7686cc4c26/signal_analog-0.25.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "555f0495dcf6021fc9314360845e19df", "sha256": "e843bec7fe56bc32d6d2432112b63e4e4b558b24b3e5e59eaf0216aabe43d562" }, "downloads": -1, "filename": "signal_analog-0.25.0-py3.6.egg", "has_sig": false, "md5_digest": "555f0495dcf6021fc9314360845e19df", "packagetype": "bdist_egg", "python_version": "3.6", "requires_python": null, "size": 74552, "upload_time": "2018-04-02T20:40:49", "upload_time_iso_8601": "2018-04-02T20:40:49.155286Z", "url": "https://files.pythonhosted.org/packages/e3/91/0c1dbee7d7c2a8acc718eaf79150c98b6fe78b7785b8bf66c6bfe3cc0d3a/signal_analog-0.25.0-py3.6.egg", "yanked": false, "yanked_reason": null } ], "0.25.1": [ { "comment_text": "", "digests": { "md5": "93f6f61d903dfb692831d7ed38b307ab", "sha256": "10668375d49b3644f50d41ffdbd20b91a94f832b2f824d6c0ecb96c65ed61a79" }, "downloads": -1, "filename": "signal_analog-0.25.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "93f6f61d903dfb692831d7ed38b307ab", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 43281, "upload_time": "2018-04-02T20:40:45", "upload_time_iso_8601": "2018-04-02T20:40:45.409496Z", "url": "https://files.pythonhosted.org/packages/c6/f0/2385560c6c6fe79304e3f213ff19461dc7aea44f9d63c9eb77427190fb4d/signal_analog-0.25.1-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "6e020ef62127254be5275a2790d398c9", "sha256": "094d7e12d7e2a316e6f852d4313f9e1d52306925d470e5e73b3cb2fd5210476a" }, "downloads": -1, "filename": "signal_analog-0.25.1-py3.6.egg", "has_sig": false, "md5_digest": "6e020ef62127254be5275a2790d398c9", "packagetype": "bdist_egg", "python_version": "3.6", "requires_python": null, "size": 74625, "upload_time": "2018-04-02T20:40:50", "upload_time_iso_8601": "2018-04-02T20:40:50.231920Z", "url": "https://files.pythonhosted.org/packages/2c/4d/2f6b742a1c5d431f243c5d87241c2dc5d49fc6bca50eb04fed36d5dab0e4/signal_analog-0.25.1-py3.6.egg", "yanked": false, "yanked_reason": null } ], "1.0.0": [ { "comment_text": "", "digests": { "md5": "7ebf42e6151f96b87037cf5b24effcb2", "sha256": "8a98322d25a635bf9f3be962f687e12202c238fad36b2cc2b54b38997f31073e" }, "downloads": -1, "filename": "signal_analog-1.0.0.tar.gz", "has_sig": false, "md5_digest": "7ebf42e6151f96b87037cf5b24effcb2", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 66946, "upload_time": "2018-04-02T22:48:59", "upload_time_iso_8601": "2018-04-02T22:48:59.470567Z", "url": "https://files.pythonhosted.org/packages/24/d9/9cd84d7f192aa41fef3168f460dfa032a76b1ca272269685b5392c1462a0/signal_analog-1.0.0.tar.gz", "yanked": false, "yanked_reason": null } ], "1.1.0": [ { "comment_text": "", "digests": { "md5": "2f2c5c9e3d20be6011773a07708f737b", "sha256": "653ff8eb41ff41ea33e5f47eac501e8206bf8a7855b75afd8a4fc3cbd94a357f" }, "downloads": -1, "filename": "signal_analog-1.1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "2f2c5c9e3d20be6011773a07708f737b", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 45776, "upload_time": "2018-04-05T16:08:50", "upload_time_iso_8601": "2018-04-05T16:08:50.442521Z", "url": "https://files.pythonhosted.org/packages/55/89/c47b0705a26549849eafabe0bad71f70216d463a9671bd5adc9c4a610b3a/signal_analog-1.1.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "1.2.0": [ { "comment_text": "", "digests": { "md5": "c82d3bb6190bd5d65ed0c9cafcb11304", "sha256": "f637db40cf255f9721d1380bfdf907a030fd9e63fa46509d52627a27dbc10986" }, "downloads": -1, "filename": "signal_analog-1.2.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "c82d3bb6190bd5d65ed0c9cafcb11304", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 46876, "upload_time": "2018-04-12T23:10:21", "upload_time_iso_8601": "2018-04-12T23:10:21.659986Z", "url": "https://files.pythonhosted.org/packages/3a/15/082161b4106320c41d1767546dfe795f33cebc61e6edd11b0ecc5d3ca0d8/signal_analog-1.2.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "1.3.0": [ { "comment_text": "", "digests": { "md5": "ddbb44dd1fc3fb8baee5443925cca3f3", "sha256": "c30965a21c6fda3cb04469c5be69f90d951192451d32922de775f55942ff04ed" }, "downloads": -1, "filename": "signal_analog-1.3.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "ddbb44dd1fc3fb8baee5443925cca3f3", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 48456, "upload_time": "2018-04-17T22:35:45", "upload_time_iso_8601": "2018-04-17T22:35:45.824810Z", "url": "https://files.pythonhosted.org/packages/2a/8c/dc934b3c0464a8290e4bcdefc1d3a181d0029dfbae29ab98bdd9e2bda963/signal_analog-1.3.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "1.4.0": [ { "comment_text": "", "digests": { "md5": "e0c7cc59c47328e1693599c7652985c7", "sha256": "7b37c5b115ca0b82071b050c03c4f20164bacb91a8ca0f9ea877ed1c3a8eac64" }, "downloads": -1, "filename": "signal_analog-1.4.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "e0c7cc59c47328e1693599c7652985c7", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 51213, "upload_time": "2018-05-10T22:07:37", "upload_time_iso_8601": "2018-05-10T22:07:37.913833Z", "url": "https://files.pythonhosted.org/packages/38/38/c6577b962b590d156bbf50a5c8d9023938d25494502e5531ae853d54c213/signal_analog-1.4.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "1.5.0": [ { "comment_text": "", "digests": { "md5": "eb41b5258d20c682d113aee5e8289386", "sha256": "b4764478f329bb51ace755f994a761d1200391092ccd1701a3c78d943bdcb5f5" }, "downloads": -1, "filename": "signal_analog-1.5.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "eb41b5258d20c682d113aee5e8289386", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 40911, "upload_time": "2018-05-17T03:29:41", "upload_time_iso_8601": "2018-05-17T03:29:41.445048Z", "url": "https://files.pythonhosted.org/packages/69/eb/676979db9649081b19f0745b5ff4a8593ed586e8ac9573769200e6a4145c/signal_analog-1.5.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "1.5.1": [ { "comment_text": "", "digests": { "md5": "c9d4313457298cea2ae31fcbf17b5734", "sha256": "82c8579920653b8fb1a3e64197b4a6ab19739422fc02a91e9185f25818542094" }, "downloads": -1, "filename": "signal_analog-1.5.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "c9d4313457298cea2ae31fcbf17b5734", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 40927, "upload_time": "2018-06-21T18:16:37", "upload_time_iso_8601": "2018-06-21T18:16:37.808729Z", "url": "https://files.pythonhosted.org/packages/f7/67/93235ce5b1ac14382ce5709560e082e6a5f5273b31b26b854ffd7bc780d8/signal_analog-1.5.1-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "1.6.0": [ { "comment_text": "", "digests": { "md5": "061edf9165636293e7f168fd2bcbe17e", "sha256": "7baade67f4db7907777e6667a3be3bf8bd7ae52875b0a5b8455f9f3cd91bcb7d" }, "downloads": -1, "filename": "signal_analog-1.6.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "061edf9165636293e7f168fd2bcbe17e", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 40999, "upload_time": "2018-07-18T18:24:00", "upload_time_iso_8601": "2018-07-18T18:24:00.256522Z", "url": "https://files.pythonhosted.org/packages/37/2c/8cde493ccf64d74badf5c34f6dc955b70332479caae25b19d18190ce41eb/signal_analog-1.6.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "2.0.0": [ { "comment_text": "", "digests": { "md5": "939d3c2c9bd81f00451f9448eab7cf61", "sha256": "ec9924e5f8f8ffbf1ee5af6874571ffd6378e70d0c9865ecbcdda2d993fb7bdb" }, "downloads": -1, "filename": "signal_analog-2.0.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "939d3c2c9bd81f00451f9448eab7cf61", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 43614, "upload_time": "2018-07-25T19:35:26", "upload_time_iso_8601": "2018-07-25T19:35:26.488110Z", "url": "https://files.pythonhosted.org/packages/21/7f/f7b8f55be8f86bde3cd12c31037451777f921500a8cadf847274806f8dcb/signal_analog-2.0.1-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "2.1.0": [ { "comment_text": "", "digests": { "md5": "540c2641afefc3369ceafa82ba11e95e", "sha256": "c307369bfc94331e9e7a6f145056f9ec3b156272144f6f77484d64417ae94f55" }, "downloads": -1, "filename": "signal_analog-2.1.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "540c2641afefc3369ceafa82ba11e95e", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 45006, "upload_time": "2018-08-21T22:32:23", "upload_time_iso_8601": "2018-08-21T22:32:23.918288Z", "url": "https://files.pythonhosted.org/packages/09/b1/fba93a7d9c044642771bd86d30d0ea32d324940231c58c9a0b59d2b92c9f/signal_analog-2.1.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "2.10.0.dev0": [ { "comment_text": "", "digests": { "md5": "22fe0ab3c6547f3c6703bf2ea137005e", "sha256": "56c49529cb92630593f12ac7be14115140f7ae761f9e8e5e64883c5edd4975de" }, "downloads": -1, "filename": "signal_analog-2.10.0.dev0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "22fe0ab3c6547f3c6703bf2ea137005e", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 56256, "upload_time": "2019-10-30T20:41:53", "upload_time_iso_8601": "2019-10-30T20:41:53.622136Z", "url": "https://files.pythonhosted.org/packages/51/bb/f7a6c920d7494bb3b6b2b0697d5e3dc2a21e55cb4c59f30b4bcdf5dcea26/signal_analog-2.10.0.dev0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "843063a06cdf54e6263d2444501e196d", "sha256": "eccbb1a78839427d8fbf85d1e23881c1cfc5d5794b5d6fd321ccfc12497111c6" }, "downloads": -1, "filename": "signal_analog-2.10.0.dev0-py3.7.egg", "has_sig": false, "md5_digest": "843063a06cdf54e6263d2444501e196d", "packagetype": "bdist_egg", "python_version": "3.7", "requires_python": null, "size": 115425, "upload_time": "2019-10-30T20:41:55", "upload_time_iso_8601": "2019-10-30T20:41:55.960551Z", "url": "https://files.pythonhosted.org/packages/73/e6/4ae877a3fd224c64d821f02a6758f37a59b21dc89c2e0c65ff6ef1e3c40a/signal_analog-2.10.0.dev0-py3.7.egg", "yanked": false, "yanked_reason": null } ], "2.10.0.dev1": [ { "comment_text": "", "digests": { "md5": "c9dc1f1396810086f8f7c093cc130d42", "sha256": "54913c131dc2dcd6a20fcc26ce0a93869161ee6fa3754928054ae398bc43e854" }, "downloads": -1, "filename": "signal_analog-2.10.0.dev1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "c9dc1f1396810086f8f7c093cc130d42", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 57012, "upload_time": "2019-10-30T20:59:02", "upload_time_iso_8601": "2019-10-30T20:59:02.002053Z", "url": "https://files.pythonhosted.org/packages/e4/69/aa41ce477a0ded3ae2cca3ae16990855384ea3ebdac175740a33b834857c/signal_analog-2.10.0.dev1-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "46e4f47e7ba80658acf50e5a1eb6496e", "sha256": "2b9ba74d2455b7c19545c7049d34a75abb7a07113ed0637ecbec8bb129b1d859" }, "downloads": -1, "filename": "signal_analog-2.10.0.dev1-py3.7.egg", "has_sig": false, "md5_digest": "46e4f47e7ba80658acf50e5a1eb6496e", "packagetype": "bdist_egg", "python_version": "3.7", "requires_python": null, "size": 117378, "upload_time": "2019-10-30T20:59:04", "upload_time_iso_8601": "2019-10-30T20:59:04.453581Z", "url": "https://files.pythonhosted.org/packages/13/db/429b6cca7af5b9c96b2db0b5551725c89781408e5b0db7293d3e20c757e7/signal_analog-2.10.0.dev1-py3.7.egg", "yanked": false, "yanked_reason": null } ], "2.2.0": [ { "comment_text": "", "digests": { "md5": "0ed9e69d986db5f6fcf9c89a4995aa6d", "sha256": "f4f29c6673026b167ee48ad9f16d8aefd064e81922893326f18c1531e3001e0e" }, "downloads": -1, "filename": "signal_analog-2.2.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "0ed9e69d986db5f6fcf9c89a4995aa6d", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 50327, "upload_time": "2018-09-28T18:26:09", "upload_time_iso_8601": "2018-09-28T18:26:09.901286Z", "url": "https://files.pythonhosted.org/packages/07/8e/6ca018c6b3e86df61327a427dff858b18987c5e9ebc07ca433cd2c396c9c/signal_analog-2.2.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "2.2.1": [ { "comment_text": "", "digests": { "md5": "188cc6fa6c0172554bb889d411eecc7c", "sha256": "d77238f10131cbe6a47151b548829aa2c929ea870b0af1eb5998187fc8f30389" }, "downloads": -1, "filename": "signal_analog-2.2.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "188cc6fa6c0172554bb889d411eecc7c", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 51767, "upload_time": "2018-10-02T20:59:39", "upload_time_iso_8601": "2018-10-02T20:59:39.849149Z", "url": "https://files.pythonhosted.org/packages/20/c8/95da5907aaa7b601d03eb9bf4f3dbc35f85a80bd0eb488af39c1377d7168/signal_analog-2.2.1-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "2.2.2": [ { "comment_text": "", "digests": { "md5": "0a47061e213b1c25448a70ba2c2ef093", "sha256": "194b8ac17180d3545d0fdd18cc514a87493542eda9a488bad5197fabd0040b9a" }, "downloads": -1, "filename": "signal_analog-2.2.2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "0a47061e213b1c25448a70ba2c2ef093", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 51787, "upload_time": "2018-10-03T17:50:04", "upload_time_iso_8601": "2018-10-03T17:50:04.139121Z", "url": "https://files.pythonhosted.org/packages/02/4d/b1f18f3d2eb2d2f8b55979dddf9a44ec68ba21f664acb4b0527ac71fd48b/signal_analog-2.2.2-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "2.3.0": [ { "comment_text": "", "digests": { "md5": "a1b5c54f9f1d4a236cb50a7e3956a984", "sha256": "0fa293afe5006440d36e5b1f17d3523d6519f8fcf85dc22b8daf5ca4c74b688b" }, "downloads": -1, "filename": "signal_analog-2.3.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "a1b5c54f9f1d4a236cb50a7e3956a984", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 50857, "upload_time": "2018-10-30T18:03:11", "upload_time_iso_8601": "2018-10-30T18:03:11.060965Z", "url": "https://files.pythonhosted.org/packages/c1/b3/e00613f75a170fabd8e7ecf173f5f9dfe80bd6b0f2ee6fcff377d8aa32b3/signal_analog-2.3.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "2.3.1": [ { "comment_text": "", "digests": { "md5": "5a65ded10ecaf971fce9b3724744888f", "sha256": "f5435e239042984e8ea73aba75bd8c6c7de94520249e4fb574eff1a7e908a908" }, "downloads": -1, "filename": "signal_analog-2.3.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "5a65ded10ecaf971fce9b3724744888f", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 50929, "upload_time": "2018-11-06T16:35:43", "upload_time_iso_8601": "2018-11-06T16:35:43.129763Z", "url": "https://files.pythonhosted.org/packages/81/91/f5ce1090d978a4ce987ab57d2e8880edc6e9cda0ae76385b2b5d982d1abe/signal_analog-2.3.1-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "2.3.2": [ { "comment_text": "", "digests": { "md5": "1c60cd28c25f364685dfaf3d756c2691", "sha256": "b37acdb4b196d3132e7f299cad85a02108d358f6284a20769bf8663a2a1f6b59" }, "downloads": -1, "filename": "signal_analog-2.3.2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "1c60cd28c25f364685dfaf3d756c2691", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 50971, "upload_time": "2018-11-12T20:41:42", "upload_time_iso_8601": "2018-11-12T20:41:42.178253Z", "url": "https://files.pythonhosted.org/packages/95/d5/8245ab10de1adea54db6f40b04cb3ee2a045d191b994811f70738b89bf2d/signal_analog-2.3.2-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "2.4.0": [ { "comment_text": "", "digests": { "md5": "8519996deb4479c21727943970078887", "sha256": "af7a2c11e5cb7c2a99b37005458ab60c22944c6872bf752d718fe495bbca5b23" }, "downloads": -1, "filename": "signal_analog-2.4.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "8519996deb4479c21727943970078887", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 52172, "upload_time": "2018-12-18T16:47:59", "upload_time_iso_8601": "2018-12-18T16:47:59.342105Z", "url": "https://files.pythonhosted.org/packages/bd/40/0a54a18ce72fd1c6d8846c657307d5a6e1714d52f64d797913c09dbedb74/signal_analog-2.4.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "2.5.0": [ { "comment_text": "", "digests": { "md5": "9a5ae96a3e291926f007eda418f9892d", "sha256": "ae4e86a57e0579486219007b69eda04f3f01cc1f3617b2f76d64dc8b0e6c7e06" }, "downloads": -1, "filename": "signal_analog-2.5.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "9a5ae96a3e291926f007eda418f9892d", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 54236, "upload_time": "2019-03-20T21:57:09", "upload_time_iso_8601": "2019-03-20T21:57:09.897035Z", "url": "https://files.pythonhosted.org/packages/87/d7/ca2bf70a04dbf74fe925739fa29b6cf91ffb42239b75bcfe1940faedcca2/signal_analog-2.5.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "1c729ee55cbb9ed06a203535eab5c7d0", "sha256": "02f14cbd81ca5a1e5614426fbc59aa5ffb6917a67c468701c8547cc10aa3c55a" }, "downloads": -1, "filename": "signal_analog-2.5.0-py3.7.egg", "has_sig": false, "md5_digest": "1c729ee55cbb9ed06a203535eab5c7d0", "packagetype": "bdist_egg", "python_version": "3.7", "requires_python": null, "size": 111752, "upload_time": "2019-03-20T21:57:11", "upload_time_iso_8601": "2019-03-20T21:57:11.580474Z", "url": "https://files.pythonhosted.org/packages/ca/65/20920ccf9a6c30746ded6d0205eef6b5ae68bbd0ce8b85499d6931773da8/signal_analog-2.5.0-py3.7.egg", "yanked": false, "yanked_reason": null } ], "2.7.0": [ { "comment_text": "", "digests": { "md5": "8b02f27a6a8bb1fa6957fdb241c4c80c", "sha256": "5a135c9b140ca223ed0a85e1392fc89db218f57e124330ac6bfa11f3873d9ad3" }, "downloads": -1, "filename": "signal_analog-2.7.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "8b02f27a6a8bb1fa6957fdb241c4c80c", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 55269, "upload_time": "2019-04-03T23:50:50", "upload_time_iso_8601": "2019-04-03T23:50:50.015054Z", "url": "https://files.pythonhosted.org/packages/d9/2a/17415e852b6c4db2b5dbc457c12e864f3672309951559539bf27883d9ed1/signal_analog-2.7.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "832ab00b67dec364d5a352070f3ae77b", "sha256": "371a298a0c7d34f9bf43c77c90c007a2b637beaf6bd1ffd6322dfe7eb655cacf" }, "downloads": -1, "filename": "signal_analog-2.7.0-py3.7.egg", "has_sig": false, "md5_digest": "832ab00b67dec364d5a352070f3ae77b", "packagetype": "bdist_egg", "python_version": "3.7", "requires_python": null, "size": 113668, "upload_time": "2019-04-03T23:50:51", "upload_time_iso_8601": "2019-04-03T23:50:51.875532Z", "url": "https://files.pythonhosted.org/packages/77/54/b92b357665eb2e7ffa02f112462fa8789e6bc4d63a1425018c43befd3426/signal_analog-2.7.0-py3.7.egg", "yanked": false, "yanked_reason": null } ], "2.7.1": [ { "comment_text": "", "digests": { "md5": "4d3cf60edc3b5fcfdacbf23e5a74ff8f", "sha256": "7405aae9efb2bf961fcab904de662973ddf01a19145120813b02e0455a9422ae" }, "downloads": -1, "filename": "signal_analog-2.7.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "4d3cf60edc3b5fcfdacbf23e5a74ff8f", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 55287, "upload_time": "2019-04-03T23:54:57", "upload_time_iso_8601": "2019-04-03T23:54:57.817881Z", "url": "https://files.pythonhosted.org/packages/b9/9f/4cd21a7971c4e3c7f1cac109e97ae817f2eb867b51e625ed69c430cec161/signal_analog-2.7.1-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "753558cbe99d0bba3688e3d07986b618", "sha256": "3a61e71b353b1ecf60447656fc6a774a372e2820c7afeafa463c2fc355bef6d9" }, "downloads": -1, "filename": "signal_analog-2.7.1-py3.7.egg", "has_sig": false, "md5_digest": "753558cbe99d0bba3688e3d07986b618", "packagetype": "bdist_egg", "python_version": "3.7", "requires_python": null, "size": 113690, "upload_time": "2019-04-03T23:54:59", "upload_time_iso_8601": "2019-04-03T23:54:59.499847Z", "url": "https://files.pythonhosted.org/packages/a2/09/59740e7b76953fe4fbaee82f08323f56c39adcfe4afc9bdcfc0e37da992d/signal_analog-2.7.1-py3.7.egg", "yanked": false, "yanked_reason": null } ], "2.7.2": [ { "comment_text": "", "digests": { "md5": "78b7596500d0e9cbfc5404592cf8dbe1", "sha256": "023d96c84c61d55a4d2ef31caeb4780a9c816f65c29062a5c75d147965d3c0c2" }, "downloads": -1, "filename": "signal_analog-2.7.2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "78b7596500d0e9cbfc5404592cf8dbe1", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 55896, "upload_time": "2019-05-14T15:15:14", "upload_time_iso_8601": "2019-05-14T15:15:14.624497Z", "url": "https://files.pythonhosted.org/packages/c2/b0/45a5ae210610675d6886664aadc6a2ae27a0af8e46b5ecdc70fa07f4a023/signal_analog-2.7.2-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "4ada28b29080676181aec0858686a4e4", "sha256": "fa748fc85deb1e78c73fcfaf0b51452b4b298246a3c8bc08f7e87a421c2e36d8" }, "downloads": -1, "filename": "signal_analog-2.7.2-py3.7.egg", "has_sig": false, "md5_digest": "4ada28b29080676181aec0858686a4e4", "packagetype": "bdist_egg", "python_version": "3.7", "requires_python": null, "size": 114328, "upload_time": "2019-05-14T15:15:16", "upload_time_iso_8601": "2019-05-14T15:15:16.713844Z", "url": "https://files.pythonhosted.org/packages/d7/58/8490aae6393155e43fa79e18c6828bbeff7bbcf5eae7b4286bce9e95e6c8/signal_analog-2.7.2-py3.7.egg", "yanked": false, "yanked_reason": null } ], "2.7.2.dev0": [ { "comment_text": "", "digests": { "md5": "efbe4a04bec78177ff88f6d48d0ac487", "sha256": "d649fcd4aece714f8666c81c7312e5c2e95c24c5fda35223ea7902f352225146" }, "downloads": -1, "filename": "signal_analog-2.7.2.dev0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "efbe4a04bec78177ff88f6d48d0ac487", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 55837, "upload_time": "2019-05-13T16:21:57", "upload_time_iso_8601": "2019-05-13T16:21:57.013328Z", "url": "https://files.pythonhosted.org/packages/d8/4b/8c11fc39eb32c6ed03568e9a7be076d2183f61ed72c9704e1265a089eda9/signal_analog-2.7.2.dev0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "ad854b3ee17cf03ccd8cc118bf35fdd2", "sha256": "fdf505de723a539949731dea74c5ffd051265873b7073f08050143d60c4a111f" }, "downloads": -1, "filename": "signal_analog-2.7.2.dev0-py3.7.egg", "has_sig": false, "md5_digest": "ad854b3ee17cf03ccd8cc118bf35fdd2", "packagetype": "bdist_egg", "python_version": "3.7", "requires_python": null, "size": 114139, "upload_time": "2019-05-13T16:21:59", "upload_time_iso_8601": "2019-05-13T16:21:59.282596Z", "url": "https://files.pythonhosted.org/packages/99/67/f6df9256f8a4abb396d02736e61c71843764faa3b596018cda3f6b959e01/signal_analog-2.7.2.dev0-py3.7.egg", "yanked": false, "yanked_reason": null } ], "2.8.0": [ { "comment_text": "", "digests": { "md5": "906f85b2fd7a16aed4c65025e414673b", "sha256": "3f900a68500ae8747b95ecbe1ac70e4b3cc645fe0dd54b369cd8afde9d0c0a08" }, "downloads": -1, "filename": "signal_analog-2.8.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "906f85b2fd7a16aed4c65025e414673b", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 55944, "upload_time": "2019-09-06T21:59:29", "upload_time_iso_8601": "2019-09-06T21:59:29.531900Z", "url": "https://files.pythonhosted.org/packages/84/fd/3acd1175523fa7bd1cf2b446a43f31646d7d48b912dc376d0715c5cb4309/signal_analog-2.8.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "83d87d979f7643cd792acccbe9701adf", "sha256": "1308ee334c84d3aa22e2bbecda15609b1590c00cc9d14bb6d169742a25ac7ada" }, "downloads": -1, "filename": "signal_analog-2.8.0-py3.7.egg", "has_sig": false, "md5_digest": "83d87d979f7643cd792acccbe9701adf", "packagetype": "bdist_egg", "python_version": "3.7", "requires_python": null, "size": 114839, "upload_time": "2019-09-06T21:59:33", "upload_time_iso_8601": "2019-09-06T21:59:33.185376Z", "url": "https://files.pythonhosted.org/packages/1b/fd/f940894babecb74a2be2869ce777091c33b30359de6cb8c8d12ffb13c704/signal_analog-2.8.0-py3.7.egg", "yanked": false, "yanked_reason": null } ], "2.9.0": [ { "comment_text": "", "digests": { "md5": "3ecba1c45823108cdc5e50796b9f8d05", "sha256": "76b38d12b7ffa2f55aeae0280eda158a2e32540fab6cbca8dc309d2732e72ad0" }, "downloads": -1, "filename": "signal_analog-2.9.0-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "3ecba1c45823108cdc5e50796b9f8d05", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 56065, "upload_time": "2019-09-26T21:23:38", "upload_time_iso_8601": "2019-09-26T21:23:38.458390Z", "url": "https://files.pythonhosted.org/packages/05/f3/a3519f519c77bd707493c10873eed3cfe6a2b148bae89af5834398350e3e/signal_analog-2.9.0-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "dd18d6734a43eba9c8566fc8506afff4", "sha256": "0d729e49a4a889bdbc878f3a9c2ab26cf7507899023d665e8c5fe5b05cf9f7ca" }, "downloads": -1, "filename": "signal_analog-2.9.0-py3.7.egg", "has_sig": false, "md5_digest": "dd18d6734a43eba9c8566fc8506afff4", "packagetype": "bdist_egg", "python_version": "3.7", "requires_python": null, "size": 115095, "upload_time": "2019-09-26T21:23:40", "upload_time_iso_8601": "2019-09-26T21:23:40.309099Z", "url": "https://files.pythonhosted.org/packages/eb/f9/42ed2912d3120bcc9225f98caf63fdbceef91be4862a69d51f73c384961b/signal_analog-2.9.0-py3.7.egg", "yanked": false, "yanked_reason": null } ], "2.9.1": [ { "comment_text": "", "digests": { "md5": "1983d4f3cf840140cae4b6fc03aef812", "sha256": "72fdeb368dacf9c96f88137fb9989acdb6d9c8ef3df945f0e1c6d66b30e23c27" }, "downloads": -1, "filename": "signal_analog-2.9.1-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "1983d4f3cf840140cae4b6fc03aef812", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 56936, "upload_time": "2019-10-30T21:40:42", "upload_time_iso_8601": "2019-10-30T21:40:42.306897Z", "url": "https://files.pythonhosted.org/packages/e6/8e/3c7f6a30415ce8ea2e5b6b2936f743a03b7c91fbf24a206ca54a0c504c3e/signal_analog-2.9.1-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "1d8015fa0afb54712de3fae69c2c6557", "sha256": "c42e576df0caf5590b48d03d484c3e4630f28ad7372c547b5de317334529b087" }, "downloads": -1, "filename": "signal_analog-2.9.1-py3.7.egg", "has_sig": false, "md5_digest": "1d8015fa0afb54712de3fae69c2c6557", "packagetype": "bdist_egg", "python_version": "3.7", "requires_python": null, "size": 117361, "upload_time": "2019-10-30T21:40:44", "upload_time_iso_8601": "2019-10-30T21:40:44.032526Z", "url": "https://files.pythonhosted.org/packages/0e/d3/98e1dfed73f0831f300f31ebe558f19bfd7f7b6e0604f15c09c7df5c41c4/signal_analog-2.9.1-py3.7.egg", "yanked": false, "yanked_reason": null } ], "2.9.2": [ { "comment_text": "", "digests": { "md5": "8a5081dbc7ff6f7b0312c38867ef9d4e", "sha256": "685731e46dcb2f8caed20f556bf5829fd756909377b61664c789406bf3a02e8f" }, "downloads": -1, "filename": "signal_analog-2.9.2-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "8a5081dbc7ff6f7b0312c38867ef9d4e", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 57062, "upload_time": "2019-11-12T16:49:16", "upload_time_iso_8601": "2019-11-12T16:49:16.380305Z", "url": "https://files.pythonhosted.org/packages/85/f8/a0eb8a8b8f00736cd1ea11073cdb41c722f8ad5f1bf705d9bb5d5a9883e3/signal_analog-2.9.2-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null }, { "comment_text": "", "digests": { "md5": "fe84b2b819acda0f12288168108d9989", "sha256": "b8f9d4b25ef9e9a1f25e194cdef80ab2265df38143bdd14860ab377d982f06a5" }, "downloads": -1, "filename": "signal_analog-2.9.2-py3.7.egg", "has_sig": false, "md5_digest": "fe84b2b819acda0f12288168108d9989", "packagetype": "bdist_egg", "python_version": "3.7", "requires_python": null, "size": 117509, "upload_time": "2019-11-12T16:49:18", "upload_time_iso_8601": "2019-11-12T16:49:18.166782Z", "url": "https://files.pythonhosted.org/packages/ba/d8/5e792fb5f11549a144991facf312939b8c9bdb51f876c2cdd62482d2e207/signal_analog-2.9.2-py3.7.egg", "yanked": false, "yanked_reason": null } ], "2.9.3": [ { "comment_text": "", "digests": { "md5": "4fd1bb9a45c5534647f2c0816005169c", "sha256": "ba2bef266ba7edc0e4471a4755be37eb0563124b64256a64155982cb6c601717" }, "downloads": -1, "filename": "signal_analog-2.9.3-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "4fd1bb9a45c5534647f2c0816005169c", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 57872, "upload_time": "2020-03-26T19:49:58", "upload_time_iso_8601": "2020-03-26T19:49:58.306790Z", "url": "https://files.pythonhosted.org/packages/1f/53/60d132580f518415cc556c7dc7fa69f088e29157a5b8b9996e4b3e86bc7f/signal_analog-2.9.3-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "4fd1bb9a45c5534647f2c0816005169c", "sha256": "ba2bef266ba7edc0e4471a4755be37eb0563124b64256a64155982cb6c601717" }, "downloads": -1, "filename": "signal_analog-2.9.3-py2.py3-none-any.whl", "has_sig": false, "md5_digest": "4fd1bb9a45c5534647f2c0816005169c", "packagetype": "bdist_wheel", "python_version": "py2.py3", "requires_python": null, "size": 57872, "upload_time": "2020-03-26T19:49:58", "upload_time_iso_8601": "2020-03-26T19:49:58.306790Z", "url": "https://files.pythonhosted.org/packages/1f/53/60d132580f518415cc556c7dc7fa69f088e29157a5b8b9996e4b3e86bc7f/signal_analog-2.9.3-py2.py3-none-any.whl", "yanked": false, "yanked_reason": null } ], "vulnerabilities": [] }