From 85af5664d4e196dca131ca587ba29185ce267b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20L=C3=AA-Qu=C3=B4c?= Date: Mon, 7 Oct 2013 13:57:40 -0700 Subject: [PATCH 01/48] Initial commit --- LICENSE | 191 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 4 ++ 2 files changed, 195 insertions(+) create mode 100644 LICENSE create mode 100644 README.md diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..37ec93a14 --- /dev/null +++ b/LICENSE @@ -0,0 +1,191 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and +distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright +owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities +that control, are controlled by, or are under common control with that entity. +For the purposes of this definition, "control" means (i) the power, direct or +indirect, to cause the direction or management of such entity, whether by +contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the +outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising +permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including +but not limited to software source code, documentation source, and configuration +files. + +"Object" form shall mean any form resulting from mechanical transformation or +translation of a Source form, including but not limited to compiled object code, +generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made +available under the License, as indicated by a copyright notice that is included +in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that +is based on (or derived from) the Work and for which the editorial revisions, +annotations, elaborations, or other modifications represent, as a whole, an +original work of authorship. For the purposes of this License, Derivative Works +shall not include works that remain separable from, or merely link (or bind by +name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version +of the Work and any modifications or additions to that Work or Derivative Works +thereof, that is intentionally submitted to Licensor for inclusion in the Work +by the copyright owner or by an individual or Legal Entity authorized to submit +on behalf of the copyright owner. For the purposes of this definition, +"submitted" means any form of electronic, verbal, or written communication sent +to the Licensor or its representatives, including but not limited to +communication on electronic mailing lists, source code control systems, and +issue tracking systems that are managed by, or on behalf of, the Licensor for +the purpose of discussing and improving the Work, but excluding communication +that is conspicuously marked or otherwise designated in writing by the copyright +owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf +of whom a Contribution has been received by Licensor and subsequently +incorporated within the Work. + +2. Grant of Copyright License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the Work and such +Derivative Works in Source or Object form. + +3. Grant of Patent License. + +Subject to the terms and conditions of this License, each Contributor hereby +grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, +irrevocable (except as stated in this section) patent license to make, have +made, use, offer to sell, sell, import, and otherwise transfer the Work, where +such license applies only to those patent claims licensable by such Contributor +that are necessarily infringed by their Contribution(s) alone or by combination +of their Contribution(s) with the Work to which such Contribution(s) was +submitted. If You institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work or a +Contribution incorporated within the Work constitutes direct or contributory +patent infringement, then any patent licenses granted to You under this License +for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. + +You may reproduce and distribute copies of the Work or Derivative Works thereof +in any medium, with or without modifications, and in Source or Object form, +provided that You meet the following conditions: + +You must give any other recipients of the Work or Derivative Works a copy of +this License; and +You must cause any modified files to carry prominent notices stating that You +changed the files; and +You must retain, in the Source form of any Derivative Works that You distribute, +all copyright, patent, trademark, and attribution notices from the Source form +of the Work, excluding those notices that do not pertain to any part of the +Derivative Works; and +If the Work includes a "NOTICE" text file as part of its distribution, then any +Derivative Works that You distribute must include a readable copy of the +attribution notices contained within such NOTICE file, excluding those notices +that do not pertain to any part of the Derivative Works, in at least one of the +following places: within a NOTICE text file distributed as part of the +Derivative Works; within the Source form or documentation, if provided along +with the Derivative Works; or, within a display generated by the Derivative +Works, if and wherever such third-party notices normally appear. The contents of +the NOTICE file are for informational purposes only and do not modify the +License. You may add Your own attribution notices within Derivative Works that +You distribute, alongside or as an addendum to the NOTICE text from the Work, +provided that such additional attribution notices cannot be construed as +modifying the License. +You may add Your own copyright statement to Your modifications and may provide +additional or different license terms and conditions for use, reproduction, or +distribution of Your modifications, or for any such Derivative Works as a whole, +provided Your use, reproduction, and distribution of the Work otherwise complies +with the conditions stated in this License. + +5. Submission of Contributions. + +Unless You explicitly state otherwise, any Contribution intentionally submitted +for inclusion in the Work by You to the Licensor shall be under the terms and +conditions of this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify the terms of +any separate license agreement you may have executed with Licensor regarding +such Contributions. + +6. Trademarks. + +This License does not grant permission to use the trade names, trademarks, +service marks, or product names of the Licensor, except as required for +reasonable and customary use in describing the origin of the Work and +reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. + +Unless required by applicable law or agreed to in writing, Licensor provides the +Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, +including, without limitation, any warranties or conditions of TITLE, +NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are +solely responsible for determining the appropriateness of using or +redistributing the Work and assume any risks associated with Your exercise of +permissions under this License. + +8. Limitation of Liability. + +In no event and under no legal theory, whether in tort (including negligence), +contract, or otherwise, unless required by applicable law (such as deliberate +and grossly negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, incidental, +or consequential damages of any character arising as a result of this License or +out of the use or inability to use the Work (including but not limited to +damages for loss of goodwill, work stoppage, computer failure or malfunction, or +any and all other commercial damages or losses), even if such Contributor has +been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. + +While redistributing the Work or Derivative Works thereof, You may choose to +offer, and charge a fee for, acceptance of support, warranty, indemnity, or +other liability obligations and/or rights consistent with this License. However, +in accepting such obligations, You may act only on Your own behalf and on Your +sole responsibility, not on behalf of any other Contributor, and only if You +agree to indemnify, defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason of your +accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work + +To apply the Apache License to your work, attach the following boilerplate +notice, with the fields enclosed by brackets "[]" replaced with your own +identifying information. (Don't include the brackets!) The text should be +enclosed in the appropriate comment syntax for the file format. We also +recommend that a file or class name and description of purpose be included on +the same "printed page" as the copyright notice for easier identification within +third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 000000000..aee6de64c --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +support-engineer +================ + +Coding Challenge From 1082ea4b9e61ecb8fcecb58ebaed4fa5a6a6affd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20L=C3=AA-Qu=C3=B4c?= Date: Mon, 7 Oct 2013 17:04:05 -0400 Subject: [PATCH 02/48] Update README.md --- README.md | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index aee6de64c..52f22abcc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,34 @@ -support-engineer -================ +# The Challenge -Coding Challenge +## Questions +1. Sign up for Datadog, get the agent reporting metrics from your local machine. Bonus question- what is the agent? + +2. Submit an event via the API. + +3. Get an event to appear in your email inbox (the email address you signed up for the account with) + +4. Technical Challenge: + * Take a simple web app (in any language) that you've already built and instrument your code with dogstatsd. + * While running a load test for a few minutes, visualize page views per second. Send us the link to this graph! + * Create a histogram to see the latency; also give us the link to the graph + * Bonus points for putting together more creative dashboards. + +If you need a tool to load-test your web app you can use tools like: +* [ab](https://httpd.apache.org/docs/2.2/programs/ab.html) +* [tsung](http://tsung.erlang-projects.org/user_manual.html#htoc2) + +## Instructions +If you have a question, create an issue in this repository. + +To submit your answers: + +1. Fork this repo. +2. Answer the questions in `answers.md` +3. Commit your code for question #3. +4. Submit a pull request. + +# References: +* [API docs](http://docs.datadoghq.com/api) +* [Guide to the Agent](http://docs.datadoghq.com/guides/basic_agent_usage/) +* [Libraries](http://docs.datadoghq.com/libraries/) +* [Guide to Metrics](http://docs.datadoghq.com/guides/metrics/) From 8389b7ff81ec180cb28e19d5e020bc0e192cbb2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20L=C3=AA-Qu=C3=B4c?= Date: Mon, 7 Oct 2013 17:04:36 -0400 Subject: [PATCH 03/48] Create answers.md --- answers.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 answers.md diff --git a/answers.md b/answers.md new file mode 100644 index 000000000..092b6d280 --- /dev/null +++ b/answers.md @@ -0,0 +1 @@ +Your answers to the questions go here. From 4947f612c94e5ce15c7a7e30f4b34276af2457e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20L=C3=AA-Qu=C3=B4c?= Date: Mon, 7 Oct 2013 17:06:14 -0400 Subject: [PATCH 04/48] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 52f22abcc..9faa320f0 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ 3. Get an event to appear in your email inbox (the email address you signed up for the account with) 4. Technical Challenge: - * Take a simple web app (in any language) that you've already built and instrument your code with dogstatsd. + * Take a simple web app ([in any language](http://docs.datadoghq.com/libraries/)) that you've already built and instrument your code with dogstatsd. * While running a load test for a few minutes, visualize page views per second. Send us the link to this graph! * Create a histogram to see the latency; also give us the link to the graph * Bonus points for putting together more creative dashboards. From 3380871236221fc7b02157205e8b92191f70bc2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20L=C3=AA-Qu=C3=B4c?= Date: Mon, 7 Oct 2013 17:08:10 -0400 Subject: [PATCH 05/48] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9faa320f0..c81de6c37 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,9 @@ To submit your answers: 1. Fork this repo. 2. Answer the questions in `answers.md` -3. Commit your code for question #3. +3. Commit your code for question #4. 4. Submit a pull request. +5. Don't forget to include links to your dashboard(s) # References: * [API docs](http://docs.datadoghq.com/api) From 8c90d4a31bb32a13f5e0b44eef80a4087a013676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20L=C3=AA-Qu=C3=B4c?= Date: Mon, 7 Oct 2013 21:57:49 -0400 Subject: [PATCH 06/48] Update README.md --- README.md | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index c81de6c37..f629d57a2 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,24 @@ +If you want to apply as a support engineer at [Datadog](http://datadog.com) you are in the right spot. + + +_DSC4652 + # The Challenge ## Questions -1. Sign up for Datadog, get the agent reporting metrics from your local machine. Bonus question- what is the agent? -2. Submit an event via the API. +### Level 1 + +* Sign up for Datadog, get the agent reporting metrics from your local machine. Bonus question: what is the agent? +* Submit an event via the API. +* Get an event to appear in your email inbox (the email address you signed up for the account with) -3. Get an event to appear in your email inbox (the email address you signed up for the account with) +### Level 2 -4. Technical Challenge: - * Take a simple web app ([in any language](http://docs.datadoghq.com/libraries/)) that you've already built and instrument your code with dogstatsd. - * While running a load test for a few minutes, visualize page views per second. Send us the link to this graph! - * Create a histogram to see the latency; also give us the link to the graph - * Bonus points for putting together more creative dashboards. +* Take a simple web app ([in any of our supported languages](http://docs.datadoghq.com/libraries/)) that you've already built and instrument your code with dogstatsd. +* While running a load test for a few minutes, visualize page views per second. Send us the link to this graph! +* Create a histogram to see the latency; also give us the link to the graph +* Bonus points for putting together more creative dashboards. If you need a tool to load-test your web app you can use tools like: * [ab](https://httpd.apache.org/docs/2.2/programs/ab.html) From 4fd69cf8f999951eb19b5e76b36c951d09d39437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20L=C3=AA-Qu=C3=B4c?= Date: Mon, 7 Oct 2013 21:58:37 -0400 Subject: [PATCH 07/48] Update README.md --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f629d57a2..3eeb360f4 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,8 @@ To submit your answers: 4. Submit a pull request. 5. Don't forget to include links to your dashboard(s) -# References: +## References + * [API docs](http://docs.datadoghq.com/api) * [Guide to the Agent](http://docs.datadoghq.com/guides/basic_agent_usage/) * [Libraries](http://docs.datadoghq.com/libraries/) From 7d510f0c2047a3af3840b74d5542b7cdd6a47111 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20L=C3=AA-Qu=C3=B4c?= Date: Thu, 10 Oct 2013 16:12:33 -0400 Subject: [PATCH 08/48] Added more levels --- README.md | 51 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 3eeb360f4..cc03110e9 100644 --- a/README.md +++ b/README.md @@ -15,14 +15,38 @@ If you want to apply as a support engineer at [Datadog](http://datadog.com) you ### Level 2 -* Take a simple web app ([in any of our supported languages](http://docs.datadoghq.com/libraries/)) that you've already built and instrument your code with dogstatsd. -* While running a load test for a few minutes, visualize page views per second. Send us the link to this graph! +* Take a simple web app ([in any of our supported languages](http://docs.datadoghq.com/libraries/)) that you've already built and instrument your code with dogstatsd. This will create **metrics**. +* While running a load test (see References) for a few minutes, visualize page views per second. Send us the link to this graph! * Create a histogram to see the latency; also give us the link to the graph * Bonus points for putting together more creative dashboards. -If you need a tool to load-test your web app you can use tools like: -* [ab](https://httpd.apache.org/docs/2.2/programs/ab.html) -* [tsung](http://tsung.erlang-projects.org/user_manual.html#htoc2) +### Level 3 + +Using the same web app from level 2: +* tag your metrics with `support` (one tag for all metrics) +* tag your metrics per page (e.g. metrics generated on `/` can be tagged with `page:home`, `/page1` with `page:page1`) +* visualize the latency by page on a graph (using stacked areas, with one color per `page`) + +### Level 4 + +Same web app: +* count the overall number of page views using dogstatsd counters. +* count the number of page views, split by page (hint: use tags) +* visualize the results on a graph +* Bonus question: do you know why the graphs are very spiky? + +### Level 5 + +Let's switch to the agent. + +* Write an agent check that samples a random value. Call this new metric: `test.support.random` + +Here is a snippet that prints a random value in python: + +```python +import random +print(random.random()) +``` ## Instructions If you have a question, create an issue in this repository. @@ -37,7 +61,24 @@ To submit your answers: ## References +### How to get started with Datadog + +* [Datadog overview](http://docs.datadoghq.com/overview/) +* [Guide to graphing in Datadog](http://docs.datadoghq.com/graphing/) + +### The Datadog API and clients + * [API docs](http://docs.datadoghq.com/api) * [Guide to the Agent](http://docs.datadoghq.com/guides/basic_agent_usage/) * [Libraries](http://docs.datadoghq.com/libraries/) * [Guide to Metrics](http://docs.datadoghq.com/guides/metrics/) + +### Extending the Agent + +* [Writing an agent check](http://docs.datadoghq.com/guides/agent_checks/) + +### Tools you may need + +Load testing +* [ab](https://httpd.apache.org/docs/2.2/programs/ab.html) +* [tsung](http://tsung.erlang-projects.org/user_manual.html#htoc2) From 581fbfe8ebd6528a4bacfd0acc3cfeb04bb13ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20L=C3=AA-Qu=C3=B4c?= Date: Thu, 10 Oct 2013 16:13:48 -0400 Subject: [PATCH 09/48] Update README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cc03110e9..3717e72e7 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,14 @@ If you want to apply as a support engineer at [Datadog](http://datadog.com) you # The Challenge +Read on and don't forget to read the **References**. + ## Questions ### Level 1 -* Sign up for Datadog, get the agent reporting metrics from your local machine. Bonus question: what is the agent? +* Sign up for Datadog, get the agent reporting metrics from your local machine. +* Bonus question: what is the agent? * Submit an event via the API. * Get an event to appear in your email inbox (the email address you signed up for the account with) From f2d836cb531bf661676a8f878227aea4c0d37210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20L=C3=AA-Qu=C3=B4c?= Date: Thu, 10 Oct 2013 16:15:26 -0400 Subject: [PATCH 10/48] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3717e72e7..923ccec33 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -If you want to apply as a support engineer at [Datadog](http://datadog.com) you are in the right spot. +If you want to apply as a support engineer at [Datadog](http://datadog.com) you are in the right spot. Go on, it's fun, I promise. _DSC4652 From f4ad427e5acc5b61c333bc66c74385a05a689abf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20L=C3=AA-Qu=C3=B4c?= Date: Thu, 10 Oct 2013 16:15:40 -0400 Subject: [PATCH 11/48] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 923ccec33..f43ca35ea 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -If you want to apply as a support engineer at [Datadog](http://datadog.com) you are in the right spot. Go on, it's fun, I promise. +If you want to apply as a support engineer at [Datadog](http://datadog.com) you are in the right spot. Read on, it's fun, I promise. _DSC4652 # The Challenge -Read on and don't forget to read the **References**. +Don't forget to read the **References**. ## Questions From e3339fcef1c3744c9071bbe71afb463d7b7391c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20L=C3=AA-Qu=C3=B4c?= Date: Mon, 7 Jul 2014 14:54:42 -0400 Subject: [PATCH 12/48] Neutral master readme. --- README.md | 87 +++++++------------------------------------------------ 1 file changed, 11 insertions(+), 76 deletions(-) diff --git a/README.md b/README.md index f43ca35ea..552416c0a 100644 --- a/README.md +++ b/README.md @@ -1,87 +1,22 @@ -If you want to apply as a support engineer at [Datadog](http://datadog.com) you are in the right spot. Read on, it's fun, I promise. - +If you want to apply as an engineer at [Datadog](http://datadog.com) you are in the right spot. _DSC4652 -# The Challenge - -Don't forget to read the **References**. - -## Questions - -### Level 1 - -* Sign up for Datadog, get the agent reporting metrics from your local machine. -* Bonus question: what is the agent? -* Submit an event via the API. -* Get an event to appear in your email inbox (the email address you signed up for the account with) - -### Level 2 - -* Take a simple web app ([in any of our supported languages](http://docs.datadoghq.com/libraries/)) that you've already built and instrument your code with dogstatsd. This will create **metrics**. -* While running a load test (see References) for a few minutes, visualize page views per second. Send us the link to this graph! -* Create a histogram to see the latency; also give us the link to the graph -* Bonus points for putting together more creative dashboards. - -### Level 3 - -Using the same web app from level 2: -* tag your metrics with `support` (one tag for all metrics) -* tag your metrics per page (e.g. metrics generated on `/` can be tagged with `page:home`, `/page1` with `page:page1`) -* visualize the latency by page on a graph (using stacked areas, with one color per `page`) - -### Level 4 - -Same web app: -* count the overall number of page views using dogstatsd counters. -* count the number of page views, split by page (hint: use tags) -* visualize the results on a graph -* Bonus question: do you know why the graphs are very spiky? - -### Level 5 - -Let's switch to the agent. - -* Write an agent check that samples a random value. Call this new metric: `test.support.random` - -Here is a snippet that prints a random value in python: - -```python -import random -print(random.random()) -``` - -## Instructions -If you have a question, create an issue in this repository. - -To submit your answers: - -1. Fork this repo. -2. Answer the questions in `answers.md` -3. Commit your code for question #4. -4. Submit a pull request. -5. Don't forget to include links to your dashboard(s) - -## References +# Why work at Datadog? -### How to get started with Datadog +We tackle some of the hardest technical problems while delivering a product that "just works" for our customers. And we are backed by some of the best VCs in NYC and the world. -* [Datadog overview](http://docs.datadoghq.com/overview/) -* [Guide to graphing in Datadog](http://docs.datadoghq.com/graphing/) +Do you want to make a difference? Are you exceptional at your job, and intrinsically motivated by it? Do you eat hard problems for breakfast and find them beautifully simple solutions by lunchtime? Do you ever wish you were there in the early days of these startups everyone is talking about? -### The Datadog API and clients +If so, we want to hear from you. -* [API docs](http://docs.datadoghq.com/api) -* [Guide to the Agent](http://docs.datadoghq.com/guides/basic_agent_usage/) -* [Libraries](http://docs.datadoghq.com/libraries/) -* [Guide to Metrics](http://docs.datadoghq.com/guides/metrics/) +[source](http://jobs.datadoghq.com/) -### Extending the Agent +# The Positions -* [Writing an agent check](http://docs.datadoghq.com/guides/agent_checks/) +1. Support Engineer: check out the `support-engineer` branch of this repository. +2. Devops Evangelist: check out the `devops-evangelist` branch of this repository. -### Tools you may need +# Questions? -Load testing -* [ab](https://httpd.apache.org/docs/2.2/programs/ab.html) -* [tsung](http://tsung.erlang-projects.org/user_manual.html#htoc2) +File an issue in this repository. From 1bf5c0f12c8a778310bfbafce1a5ddd4ee47d733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20L=C3=AA-Qu=C3=B4c?= Date: Mon, 7 Jul 2014 14:57:36 -0400 Subject: [PATCH 13/48] Update README.md --- README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 552416c0a..5f64aff5a 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,15 @@ If so, we want to hear from you. # The Positions -1. Support Engineer: check out the `support-engineer` branch of this repository. -2. Devops Evangelist: check out the `devops-evangelist` branch of this repository. +1. Support Engineer: check out the `support-engineer` [branch](https://github.com/DataDog/hiring-engineers/tree/support-engineer) of this repository. +2. Devops Evangelist: check out the `devops-evangelist` [branch](https://github.com/DataDog/hiring-engineers/tree/devopsevangelist) of this repository. -# Questions? +# How do I submit my results? + +1. Fork this repository. +2. Check out the branch that interests you. +3. Commit your work. +4. Submit a pull request against this upstream repository. +# Questions? File an issue in this repository. From e7402a1f1eba6bd24bdf7b2f935bc12d6462b680 Mon Sep 17 00:00:00 2001 From: Arthur Date: Thu, 7 Aug 2014 15:22:31 -0400 Subject: [PATCH 14/48] Fix link to Devops Evangelist branch --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f64aff5a..1423f9c3b 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ If so, we want to hear from you. # The Positions 1. Support Engineer: check out the `support-engineer` [branch](https://github.com/DataDog/hiring-engineers/tree/support-engineer) of this repository. -2. Devops Evangelist: check out the `devops-evangelist` [branch](https://github.com/DataDog/hiring-engineers/tree/devopsevangelist) of this repository. +2. Devops Evangelist: check out the `devops-evangelist` [branch](https://github.com/DataDog/hiring-engineers/tree/devops-evangelist) of this repository. # How do I submit my results? From ef429c0ab2e1b0885690d6eb3c9e513f01c6fe08 Mon Sep 17 00:00:00 2001 From: pvfkb Date: Fri, 27 Jan 2017 18:05:58 -0600 Subject: [PATCH 15/48] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1423f9c3b..71137ed77 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,9 @@ If so, we want to hear from you. # The Positions 1. Support Engineer: check out the `support-engineer` [branch](https://github.com/DataDog/hiring-engineers/tree/support-engineer) of this repository. -2. Devops Evangelist: check out the `devops-evangelist` [branch](https://github.com/DataDog/hiring-engineers/tree/devops-evangelist) of this repository. +2. Solutions Engineer: check out the `solutions-engineer` [branch](https://github.com/DataDog/hiring-engineers/tree/solutions-engineer) of this repository. +3. Devops Evangelist: check out the `devops-evangelist` [branch](https://github.com/DataDog/hiring-engineers/tree/devops-evangelist) of this repository. + # How do I submit my results? From 5a692a2baf7e366ff5143083f4be1e23c682a8e7 Mon Sep 17 00:00:00 2001 From: jinmei612 Date: Sun, 15 Oct 2017 21:06:25 +1100 Subject: [PATCH 16/48] Update answers.md --- answers.md | 159 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/answers.md b/answers.md index 092b6d280..a4d57f5b8 100644 --- a/answers.md +++ b/answers.md @@ -1 +1,160 @@ Your answers to the questions go here. + + +I am here to apply for the support engineer at [Datadog](http://datadog.com) Sydney. + + +_DSC4652 + +# The Challenge + +Don't forget to read the **References**. + +## Questions + +### Level 0 (optional) - Setup an Ubuntu VM + +* While it is not required, we recommend that you spin up a fresh linux VM via Vagrant or other tools so that you don't run into any OS or dependency issues. [Here are instructions for setting up a Vagrant Ubuntu 12.04 VM.](https://www.vagrantup.com/docs/getting-started/) +>Answer: I am using macOS Sierra Version 10.12.6 for this Challenge. + + +### Level 1 - Collecting your Data + +* Sign up for Datadog (use "Datadog Recruiting Candidate" in the "Company" field), get the Agent reporting metrics from your local machine. + +>Answer: Click integration-Agent in DataDog and follow the installation instructions for Mac OS X to install the Agent on my own machine. + + +* Bonus question: In your own words, what is the Agent? + +>Answer: The Datadog agent is a full stack platform/software which brings data from different places via API to Datadog interface. + +* Add tags in the Agent config file and show us a screenshot of your host and its tags on the Host Map page in Datadog. + +>Answer: Change direction by type cd ~/.datadog-agent in terminal, then type emacs datadog.conf to open and edit the configuration file. (reference: https://docs.datadoghq.com/guides/tagging/) + + +After a few minutes refresh the Datadog, go to Infrastructure - Host Map, the tags are now shown in there. + + + +* Install a database on your machine (MongoDB, MySQL, or PostgreSQL) and then install the respective Datadog integration for that database. + +>Answer: I am using PostgreSQL for this part, for how to download and install the software PostgreSQL please refer to -> [Download PostgreSQL here](https://www.postgresql.org/download/) + +find the PostgreSQL API under Integrations, follow the installation instructions + + +Type datadog-agent info in Terminal to check states. + + +* Write a custom Agent check that samples a random value. Call this new metric: `test.support.random` + +>Answer: + +~/.datadog-agent/checks.d/randomcheck.py + +``` +import random +from checks import AgentCheck +class RandomCheck(AgentCheck): + def check(self, instance): + self.gauge('test.support.random', random.random()) +``` + +~/.datadog-agent/conf.d/randomcheck.yaml + +``` +init_config: +instances: + [{}] +``` + +reference: https://docs.datadoghq.com/guides/agent_checks/ + +Here is a snippet that prints a random value in python: + +```python +import random +print(random.random()) +``` + +### Level 2 - Visualizing your Data + +* Since your database integration is reporting now, clone your database integration dashboard and add additional database metrics to it as well as your `test.support.random` metric from the custom Agent check. + +>Answer: Go to Dashboard-Dashboard List, select Postgres, then click on Clone Dashboard on the top right corner. + + +Click on add new graph and type test.support.random  in the Get then click Save. + + +reference: https://docs.datadoghq.com/guides/templating/ + +* Bonus question: What is the difference between a timeboard and a screenboard? + +>Answer: all graphs are always scoped to the same time in timeboard, but screenboard is flexible and more customisable, it can be created by drag and drop widgets. + +reference: https://help.datadoghq.com/hc/en-us/articles/204580349-What-is-the-difference-between-a-ScreenBoard-and-a-TimeBoard- + +* Take a snapshot of your `test.support.random` graph and draw a box around a section that shows it going above 0.90. Make sure this snapshot is sent to your email by using the @notification + +>Answer: click on the camera icon on the top right of the graph and type @person + + + +### Level 3 - Alerting on your Data + +Since you've already caught your test metric going above 0.90 once, you don't want to have to continually watch this dashboard to be alerted when it goes above 0.90 again. So let's make life easier by creating a monitor. +* Set up a monitor on this metric that alerts you when it goes above 0.90 at least once during the last 5 minutes + +>Answer: click on the setting icon - create Monitor on the top right of the graph + + + + +reference: https://docs.datadoghq.com/guides/monitors/ + +* Bonus points: Make it a multi-alert by host so that you won't have to recreate it if your infrastructure scales up. + +>Answer: select Multi Alert under 2-Define the metric, has been done in the previous step. + +* Give it a descriptive monitor name and message (it might be worth it to include the link to your previously created dashboard in the message). Make sure that the monitor will notify you via email. + + + +* This monitor should alert you within 15 minutes. So when it does, take a screenshot of the email that it sends you. + + + +* Bonus: Since this monitor is going to alert pretty often, you don't want to be alerted when you are out of the office. Set up a scheduled downtime for this monitor that silences it from 7pm to 9am daily. Make sure that your email is notified when you schedule the downtime and take a screenshot of that notification. + +>Answer: Monitors - Manage Downtime - Schedule Downtime + + +## Instructions +If you have a question, create an issue in this repository. + +To submit your answers: + +1. Fork this repo. +2. Answer the questions in `answers.md` +3. Commit as much code as you need to support your answers. +4. Submit a pull request. +5. Don't forget to include links to your dashboard(s), even better links *and* screenshots. We recommend that you include your screenshots inline with your answers. + +## References + +### How to get started with Datadog + +* [Datadog overview](http://docs.datadoghq.com/overview/) +* [Guide to graphing in Datadog](http://docs.datadoghq.com/graphing/) +* [Guide to monitoring in Datadog](http://docs.datadoghq.com/guides/monitoring/) + +### The Datadog Agent and Metrics + +* [Guide to the Agent](http://docs.datadoghq.com/guides/basic_agent_usage/) +* [Writing an Agent check](http://docs.datadoghq.com/guides/agent_checks/) + +### Other questions: +* [Datadog Help Center](https://help.datadoghq.com/hc/en-us) From f58bfb5ce3676d9701172041337b7f6c1a7227a9 Mon Sep 17 00:00:00 2001 From: jinmei612 Date: Sun, 15 Oct 2017 21:11:41 +1100 Subject: [PATCH 17/48] Update answers.md --- answers.md | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/answers.md b/answers.md index a4d57f5b8..37221955e 100644 --- a/answers.md +++ b/answers.md @@ -22,7 +22,7 @@ Don't forget to read the **References**. * Sign up for Datadog (use "Datadog Recruiting Candidate" in the "Company" field), get the Agent reporting metrics from your local machine. ->Answer: Click integration-Agent in DataDog and follow the installation instructions for Mac OS X to install the Agent on my own machine. +>Answer: Click _integration-Agent_ in DataDog and follow the installation instructions for Mac OS X to install the Agent on my own machine. * Bonus question: In your own words, what is the Agent? @@ -31,10 +31,13 @@ Don't forget to read the **References**. * Add tags in the Agent config file and show us a screenshot of your host and its tags on the Host Map page in Datadog. ->Answer: Change direction by type cd ~/.datadog-agent in terminal, then type emacs datadog.conf to open and edit the configuration file. (reference: https://docs.datadoghq.com/guides/tagging/) +>Answer: Change direction by type _cd ~/.datadog-agent_ in terminal, then type _emacs datadog.conf_ to open and edit the configuration file. +reference: https://docs.datadoghq.com/guides/tagging/ + -After a few minutes refresh the Datadog, go to Infrastructure - Host Map, the tags are now shown in there. +After a few minutes refresh the Datadog, go to _Infrastructure - Host Map_, the tags are now shown in there. + @@ -42,17 +45,17 @@ After a few minutes refresh the Datadog, go to Infrastructure - Host Map, the ta >Answer: I am using PostgreSQL for this part, for how to download and install the software PostgreSQL please refer to -> [Download PostgreSQL here](https://www.postgresql.org/download/) -find the PostgreSQL API under Integrations, follow the installation instructions +find the PostgreSQL API under _Integrations_, follow the installation instructions -Type datadog-agent info in Terminal to check states. +Type _datadog-agent info_ in Terminal to check states. * Write a custom Agent check that samples a random value. Call this new metric: `test.support.random` >Answer: -~/.datadog-agent/checks.d/randomcheck.py +_~/.datadog-agent/checks.d/randomcheck.py_ ``` import random @@ -62,7 +65,7 @@ class RandomCheck(AgentCheck): self.gauge('test.support.random', random.random()) ``` -~/.datadog-agent/conf.d/randomcheck.yaml +_~/.datadog-agent/conf.d/randomcheck.yaml_ ``` init_config: @@ -83,10 +86,10 @@ print(random.random()) * Since your database integration is reporting now, clone your database integration dashboard and add additional database metrics to it as well as your `test.support.random` metric from the custom Agent check. ->Answer: Go to Dashboard-Dashboard List, select Postgres, then click on Clone Dashboard on the top right corner. +>Answer: Go to _Dashboard-Dashboard List_, select _Postgres_, then click on _Clone Dashboard_ on the top right corner. -Click on add new graph and type test.support.random  in the Get then click Save. +Click on add new graph and type _test.support.random_ in the Get then click Save. reference: https://docs.datadoghq.com/guides/templating/ @@ -108,7 +111,7 @@ reference: https://help.datadoghq.com/hc/en-us/articles/204580349-What-is-the-di Since you've already caught your test metric going above 0.90 once, you don't want to have to continually watch this dashboard to be alerted when it goes above 0.90 again. So let's make life easier by creating a monitor. * Set up a monitor on this metric that alerts you when it goes above 0.90 at least once during the last 5 minutes ->Answer: click on the setting icon - create Monitor on the top right of the graph +>Answer: click on the _setting icon - create Monitor_ on the top right of the graph @@ -117,7 +120,7 @@ reference: https://docs.datadoghq.com/guides/monitors/ * Bonus points: Make it a multi-alert by host so that you won't have to recreate it if your infrastructure scales up. ->Answer: select Multi Alert under 2-Define the metric, has been done in the previous step. +>Answer: select _Multi Alert_ under 2-Define the metric, has been done in the previous step. * Give it a descriptive monitor name and message (it might be worth it to include the link to your previously created dashboard in the message). Make sure that the monitor will notify you via email. @@ -129,9 +132,11 @@ reference: https://docs.datadoghq.com/guides/monitors/ * Bonus: Since this monitor is going to alert pretty often, you don't want to be alerted when you are out of the office. Set up a scheduled downtime for this monitor that silences it from 7pm to 9am daily. Make sure that your email is notified when you schedule the downtime and take a screenshot of that notification. ->Answer: Monitors - Manage Downtime - Schedule Downtime +>Answer: _Monitors - Manage Downtime - Schedule Downtime_ + + ## Instructions If you have a question, create an issue in this repository. From a283dfe45668f12e3cb6fb4bc76d1fbfa717211d Mon Sep 17 00:00:00 2001 From: jinmei612 Date: Fri, 20 Oct 2017 23:35:54 +1100 Subject: [PATCH 18/48] Update answers.md --- answers.md | 53 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/answers.md b/answers.md index 37221955e..5ac605e1e 100644 --- a/answers.md +++ b/answers.md @@ -1,15 +1,26 @@ Your answers to the questions go here. +# About Datadog +Datadog is a data monitoring service for cloud-scale applications, bringing together data from servers, databases, tools, and services to present a unified view of an entire stack. These capabilities are provided on a SaaS-based data analytics platform. [Wiki](https://en.wikipedia.org/wiki/Datadog) + +### Features +* Observability - From infrastructure to apps, in any environment +* Dashboards - Use instant, real-time boards or build your own +* Infrastructure - From overview to deep details, fast +* Analytics - Custom app metrics or business KPIs +* Collaboration - Share data, discuss in context, solve issues quickly +* Alerts - Avoid alert fatigue with smart, actionable alerts +* API - Love infrastructure as code? You'll love Datadog's API +* Machine Learning - Automatically detect outliers and temporal anomalies +* APM - Monitor, optimize, and troubleshoot app performance + +_DSC4652 I am here to apply for the support engineer at [Datadog](http://datadog.com) Sydney. - -_DSC4652 # The Challenge -Don't forget to read the **References**. - ## Questions ### Level 0 (optional) - Setup an Ubuntu VM @@ -22,12 +33,19 @@ Don't forget to read the **References**. * Sign up for Datadog (use "Datadog Recruiting Candidate" in the "Company" field), get the Agent reporting metrics from your local machine. ->Answer: Click _integration-Agent_ in DataDog and follow the installation instructions for Mac OS X to install the Agent on my own machine. +>Answer: [Sign up here](https://www.datadoghq.com/#), get a datadog account for free for 14 days. + +>Once log in, click on _integration-Agent_ in DataDog on the left column and follow the installation instructions for Mac OS X to install the Agent. + +> The datadog can be installed on OS X as easily as: +``` +DD_API_KEY=63ab065b2982aed65fff538ba18a93ba bash -c "$(curl -L https://raw.githubusercontent.com/DataDog/dd-agent/master/packaging/osx/install.sh)" +``` * Bonus question: In your own words, what is the Agent? ->Answer: The Datadog agent is a full stack platform/software which brings data from different places via API to Datadog interface. +>Answer: The Datadog agent is a full stack data monitoring platform/software which brings data from servers, databases, tools, and services to the Datadog web interface. * Add tags in the Agent config file and show us a screenshot of your host and its tags on the Host Map page in Datadog. @@ -45,12 +63,24 @@ After a few minutes refresh the Datadog, go to _Infrastructure - Host Map_, the >Answer: I am using PostgreSQL for this part, for how to download and install the software PostgreSQL please refer to -> [Download PostgreSQL here](https://www.postgresql.org/download/) -find the PostgreSQL API under _Integrations_, follow the installation instructions +>Find the PostgreSQL API under _Integrations-Integrations_, click _instal_, then click on _Configuration_ tab. + +>Create a read-only datadog user with proper access to your PostgreSQL Server. +``` +create user datadog with password 'password_for_datadog'; +grant SELECT ON pg_stat_database to datadog; +``` +>Configure the Agent to connect to the PostgreSQL server +>Edit _conf.d/postgres.yaml_ -Type _datadog-agent info_ in Terminal to check states. +>Restart the Agent + +>Type _datadog-agent info_ in Terminal to check states. +>Can also go to _Dashboard-Dashboard List_ to check to see if it is working or not. + * Write a custom Agent check that samples a random value. Call this new metric: `test.support.random` >Answer: @@ -89,7 +119,7 @@ print(random.random()) >Answer: Go to _Dashboard-Dashboard List_, select _Postgres_, then click on _Clone Dashboard_ on the top right corner. -Click on add new graph and type _test.support.random_ in the Get then click Save. +>Click on add new graph and type _test.support.random_ in the Get then click Save. reference: https://docs.datadoghq.com/guides/templating/ @@ -163,3 +193,8 @@ To submit your answers: ### Other questions: * [Datadog Help Center](https://help.datadoghq.com/hc/en-us) + +### About Me +Mathematician, Computer Scientist, Barista, World Explorer + +[My Linkedin](https://www.linkedin.com/in/mei-jin/) From a526560169f1a4f57aa3f8e49d15d1f0e6fb8e36 Mon Sep 17 00:00:00 2001 From: jinmei612 Date: Sat, 21 Oct 2017 14:12:31 +1100 Subject: [PATCH 19/48] Update answers.md --- answers.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/answers.md b/answers.md index 5ac605e1e..841e48080 100644 --- a/answers.md +++ b/answers.md @@ -35,7 +35,7 @@ I am here to apply for the support engineer at [Datadog](http://datadog.com) Syd >Answer: [Sign up here](https://www.datadoghq.com/#), get a datadog account for free for 14 days. ->Once log in, click on _integration-Agent_ in DataDog on the left column and follow the installation instructions for Mac OS X to install the Agent. +>login, click on _integration-Agent_ in DataDog on the left column and follow the installation instructions for Mac OS X to install the Agent. > The datadog can be installed on OS X as easily as: ``` @@ -63,7 +63,8 @@ After a few minutes refresh the Datadog, go to _Infrastructure - Host Map_, the >Answer: I am using PostgreSQL for this part, for how to download and install the software PostgreSQL please refer to -> [Download PostgreSQL here](https://www.postgresql.org/download/) ->Find the PostgreSQL API under _Integrations-Integrations_, click _instal_, then click on _Configuration_ tab. +>Find the PostgreSQL API under _Integrations-Integrations_, click _install_, then click on _Configuration_ tab. + >Create a read-only datadog user with proper access to your PostgreSQL Server. ``` @@ -79,7 +80,7 @@ grant SELECT ON pg_stat_database to datadog; >Type _datadog-agent info_ in Terminal to check states. ->Can also go to _Dashboard-Dashboard List_ to check to see if it is working or not. +>Can also go to _Dashboard-Dashboard List_ to check whether it is working or not. * Write a custom Agent check that samples a random value. Call this new metric: `test.support.random` @@ -116,7 +117,7 @@ print(random.random()) * Since your database integration is reporting now, clone your database integration dashboard and add additional database metrics to it as well as your `test.support.random` metric from the custom Agent check. ->Answer: Go to _Dashboard-Dashboard List_, select _Postgres_, then click on _Clone Dashboard_ on the top right corner. +>Answer: Go to _Dashboard-Dashboard List_, select _Postgres_ under _Integration Dashboards_, then click on _Clone Dashboard_ on the top right corner. >Click on add new graph and type _test.support.random_ in the Get then click Save. From e02c459682ea4430bca41a76618f25c8c59aac91 Mon Sep 17 00:00:00 2001 From: jinmei612 Date: Sun, 22 Oct 2017 12:55:45 +1100 Subject: [PATCH 20/48] Update answers.md --- answers.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/answers.md b/answers.md index 841e48080..003a3de88 100644 --- a/answers.md +++ b/answers.md @@ -35,7 +35,7 @@ I am here to apply for the support engineer at [Datadog](http://datadog.com) Syd >Answer: [Sign up here](https://www.datadoghq.com/#), get a datadog account for free for 14 days. ->login, click on _integration-Agent_ in DataDog on the left column and follow the installation instructions for Mac OS X to install the Agent. +>login, click on [_integration-Agent_](https://app.datadoghq.com/account/settings#agent/mac) in DataDog on the left column and follow the installation instructions for Mac OS X to install the Agent. > The datadog can be installed on OS X as easily as: ``` @@ -54,7 +54,8 @@ reference: https://docs.datadoghq.com/guides/tagging/ -After a few minutes refresh the Datadog, go to _Infrastructure - Host Map_, the tags are now shown in there. +After a few minutes refresh the Datadog, go to [Infrastructure - Host Map_](https://app.datadoghq.com/infrastructure/map?fillby=avg%3Acpuutilization&sizeby=avg%3Anometric&groupby=none&nameby=name&nometrichosts=false&tvMode=false&nogrouphosts=false&palette=green_to_orange&paletteflip=false), the tags are now shown in there. + @@ -63,7 +64,7 @@ After a few minutes refresh the Datadog, go to _Infrastructure - Host Map_, the >Answer: I am using PostgreSQL for this part, for how to download and install the software PostgreSQL please refer to -> [Download PostgreSQL here](https://www.postgresql.org/download/) ->Find the PostgreSQL API under _Integrations-Integrations_, click _install_, then click on _Configuration_ tab. +>Find the PostgreSQL API under [_Integrations-Integrations_](https://app.datadoghq.com/account/settings), click _install_, then click on _Configuration_ tab. >Create a read-only datadog user with proper access to your PostgreSQL Server. @@ -80,7 +81,7 @@ grant SELECT ON pg_stat_database to datadog; >Type _datadog-agent info_ in Terminal to check states. ->Can also go to _Dashboard-Dashboard List_ to check whether it is working or not. +>Can also go to [_Dashboard-Dashboard List_](https://app.datadoghq.com/dash/list) to check whether it is working or not. * Write a custom Agent check that samples a random value. Call this new metric: `test.support.random` @@ -117,7 +118,7 @@ print(random.random()) * Since your database integration is reporting now, clone your database integration dashboard and add additional database metrics to it as well as your `test.support.random` metric from the custom Agent check. ->Answer: Go to _Dashboard-Dashboard List_, select _Postgres_ under _Integration Dashboards_, then click on _Clone Dashboard_ on the top right corner. +>Answer: Go to [_Dashboard-Dashboard List_](https://app.datadoghq.com/dash/list), select [_Postgres_](https://app.datadoghq.com/dash/integration/postgresql?live=true&page=0&is_auto=false&from_ts=1508633477863&to_ts=1508637077863&tile_size=m) under _Integration Dashboards_, then click on _Clone Dashboard_ on the top right corner. >Click on add new graph and type _test.support.random_ in the Get then click Save. @@ -142,7 +143,7 @@ reference: https://help.datadoghq.com/hc/en-us/articles/204580349-What-is-the-di Since you've already caught your test metric going above 0.90 once, you don't want to have to continually watch this dashboard to be alerted when it goes above 0.90 again. So let's make life easier by creating a monitor. * Set up a monitor on this metric that alerts you when it goes above 0.90 at least once during the last 5 minutes ->Answer: click on the _setting icon - create Monitor_ on the top right of the graph +>Answer: click on the [_setting icon - create Monitor_](https://app.datadoghq.com/monitors#create/metric?aggregator=avg&metric=test.support.random) on the top right of the graph @@ -163,7 +164,7 @@ reference: https://docs.datadoghq.com/guides/monitors/ * Bonus: Since this monitor is going to alert pretty often, you don't want to be alerted when you are out of the office. Set up a scheduled downtime for this monitor that silences it from 7pm to 9am daily. Make sure that your email is notified when you schedule the downtime and take a screenshot of that notification. ->Answer: _Monitors - Manage Downtime - Schedule Downtime_ +>Answer: [_Monitors - Manage Downtime - Schedule Downtime_](https://app.datadoghq.com/monitors#downtime) From 072dc126a5a69d135f1dbf3cb30e83f0be25a556 Mon Sep 17 00:00:00 2001 From: jinmei612 Date: Sun, 22 Oct 2017 12:56:33 +1100 Subject: [PATCH 21/48] Update answers.md --- answers.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/answers.md b/answers.md index 003a3de88..a6ffb2e96 100644 --- a/answers.md +++ b/answers.md @@ -54,7 +54,7 @@ reference: https://docs.datadoghq.com/guides/tagging/ -After a few minutes refresh the Datadog, go to [Infrastructure - Host Map_](https://app.datadoghq.com/infrastructure/map?fillby=avg%3Acpuutilization&sizeby=avg%3Anometric&groupby=none&nameby=name&nometrichosts=false&tvMode=false&nogrouphosts=false&palette=green_to_orange&paletteflip=false), the tags are now shown in there. +After a few minutes refresh the Datadog, go to [_Infrastructure - Host Map_](https://app.datadoghq.com/infrastructure/map?fillby=avg%3Acpuutilization&sizeby=avg%3Anometric&groupby=none&nameby=name&nometrichosts=false&tvMode=false&nogrouphosts=false&palette=green_to_orange&paletteflip=false), the tags are now shown in there. From 4bbf725e556ff584b8e9bba8e6948d5358c85ed1 Mon Sep 17 00:00:00 2001 From: jinmei612 Date: Sun, 22 Oct 2017 15:16:38 +1100 Subject: [PATCH 22/48] Update answers.md --- answers.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/answers.md b/answers.md index a6ffb2e96..502871f0e 100644 --- a/answers.md +++ b/answers.md @@ -50,7 +50,7 @@ DD_API_KEY=63ab065b2982aed65fff538ba18a93ba bash -c "$(curl -L https://raw.githu * Add tags in the Agent config file and show us a screenshot of your host and its tags on the Host Map page in Datadog. >Answer: Change direction by type _cd ~/.datadog-agent_ in terminal, then type _emacs datadog.conf_ to open and edit the configuration file. -reference: https://docs.datadoghq.com/guides/tagging/ +[reference](https://docs.datadoghq.com/guides/tagging/) @@ -105,7 +105,7 @@ instances: [{}] ``` -reference: https://docs.datadoghq.com/guides/agent_checks/ +[reference](https://docs.datadoghq.com/guides/agent_checks/) Here is a snippet that prints a random value in python: @@ -124,13 +124,13 @@ print(random.random()) >Click on add new graph and type _test.support.random_ in the Get then click Save. -reference: https://docs.datadoghq.com/guides/templating/ +[reference](https://docs.datadoghq.com/guides/templating/) * Bonus question: What is the difference between a timeboard and a screenboard? >Answer: all graphs are always scoped to the same time in timeboard, but screenboard is flexible and more customisable, it can be created by drag and drop widgets. -reference: https://help.datadoghq.com/hc/en-us/articles/204580349-What-is-the-difference-between-a-ScreenBoard-and-a-TimeBoard- +[reference](https://help.datadoghq.com/hc/en-us/articles/204580349-What-is-the-difference-between-a-ScreenBoard-and-a-TimeBoard-) * Take a snapshot of your `test.support.random` graph and draw a box around a section that shows it going above 0.90. Make sure this snapshot is sent to your email by using the @notification @@ -148,7 +148,7 @@ Since you've already caught your test metric going above 0.90 once, you don't wa -reference: https://docs.datadoghq.com/guides/monitors/ +[reference](https://docs.datadoghq.com/guides/monitors/) * Bonus points: Make it a multi-alert by host so that you won't have to recreate it if your infrastructure scales up. From 88b8f2e9f74e58b46105df82584a17a219b38e73 Mon Sep 17 00:00:00 2001 From: jinmei612 Date: Mon, 23 Oct 2017 12:05:19 +1100 Subject: [PATCH 23/48] Update answers.md --- answers.md | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/answers.md b/answers.md index 502871f0e..1a8d2434c 100644 --- a/answers.md +++ b/answers.md @@ -85,9 +85,8 @@ grant SELECT ON pg_stat_database to datadog; * Write a custom Agent check that samples a random value. Call this new metric: `test.support.random` ->Answer: - -_~/.datadog-agent/checks.d/randomcheck.py_ +>Answer: Each check will have a configuration file that will be placed in the conf.d directory. Configuration is written using YAML. The file name should match the name of the check module (e.g. randomcheck.py and randomcheck.yaml).There are two places that you will need to add files for your check. +> The first is the checks.d folder, which lives in your Agent root. _~/.datadog-agent/checks.d/randomcheck.py_ ``` import random @@ -97,7 +96,7 @@ class RandomCheck(AgentCheck): self.gauge('test.support.random', random.random()) ``` -_~/.datadog-agent/conf.d/randomcheck.yaml_ +>The other folder is conf.d which lives in the Agent configuration root. _~/.datadog-agent/conf.d/randomcheck.yaml_ ``` init_config: @@ -134,7 +133,7 @@ print(random.random()) * Take a snapshot of your `test.support.random` graph and draw a box around a section that shows it going above 0.90. Make sure this snapshot is sent to your email by using the @notification ->Answer: click on the camera icon on the top right of the graph and type @person +>Answer: click on the camera icon on the top right of the graph will get you a screenshot of the current graph, draw a box on the graph and type mesage @your_email, then the screenshot will be sent to your email address. @@ -144,7 +143,14 @@ Since you've already caught your test metric going above 0.90 once, you don't wa * Set up a monitor on this metric that alerts you when it goes above 0.90 at least once during the last 5 minutes >Answer: click on the [_setting icon - create Monitor_](https://app.datadoghq.com/monitors#create/metric?aggregator=avg&metric=test.support.random) on the top right of the graph - + + + +>1.Choose the detection method + +>2.Select the metric and scope you want to monitor + +>3.Select the alert grouping @@ -153,8 +159,13 @@ Since you've already caught your test metric going above 0.90 once, you don't wa * Bonus points: Make it a multi-alert by host so that you won't have to recreate it if your infrastructure scales up. >Answer: select _Multi Alert_ under 2-Define the metric, has been done in the previous step. +>A simple alert aggregates over all reporting sources,You will get one alert when the aggregated value meets the conditions set below. This works best to monitor a metric from a single host. A multi alert applies the alert to each source, according to your group parameters. * Give it a descriptive monitor name and message (it might be worth it to include the link to your previously created dashboard in the message). Make sure that the monitor will notify you via email. +>Give the monitor a title. It is often useful to use a succinct explanation of the monitor so a notified team member can quickly understand what is going on. + +>Enter a message for the monitor. This field allows standard markdown formatting as well as @your_email_address. + @@ -165,6 +176,13 @@ Since you've already caught your test metric going above 0.90 once, you don't wa * Bonus: Since this monitor is going to alert pretty often, you don't want to be alerted when you are out of the office. Set up a scheduled downtime for this monitor that silences it from 7pm to 9am daily. Make sure that your email is notified when you schedule the downtime and take a screenshot of that notification. >Answer: [_Monitors - Manage Downtime - Schedule Downtime_](https://app.datadoghq.com/monitors#downtime) + +>1.Choose what to silence. + +>2.Set a schedule. + +>(optional)3-4.Add an optional message to notify your team + From bc885fbd10addd2efdb05fcb41725459957a8bbc Mon Sep 17 00:00:00 2001 From: Kevin Walter Date: Wed, 27 Dec 2017 14:34:04 -0500 Subject: [PATCH 24/48] updating positions list --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 71137ed77..d057604a9 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,8 @@ If so, we want to hear from you. # The Positions -1. Support Engineer: check out the `support-engineer` [branch](https://github.com/DataDog/hiring-engineers/tree/support-engineer) of this repository. -2. Solutions Engineer: check out the `solutions-engineer` [branch](https://github.com/DataDog/hiring-engineers/tree/solutions-engineer) of this repository. -3. Devops Evangelist: check out the `devops-evangelist` [branch](https://github.com/DataDog/hiring-engineers/tree/devops-evangelist) of this repository. +1. Solutions Engineer: check out the `solutions-engineer` [branch](https://github.com/DataDog/hiring-engineers/tree/solutions-engineer) of this repository. +2. Devops Evangelist: check out the `devops-evangelist` [branch](https://github.com/DataDog/hiring-engineers/tree/devops-evangelist) of this repository. # How do I submit my results? From b27d4a5642617930747787c061e560241e087eb8 Mon Sep 17 00:00:00 2001 From: bburns-ddog Date: Tue, 11 Sep 2018 06:11:00 -0700 Subject: [PATCH 25/48] revert candidate answer merge --- answers.md | 219 ----------------------------------------------------- 1 file changed, 219 deletions(-) diff --git a/answers.md b/answers.md index 1a8d2434c..092b6d280 100644 --- a/answers.md +++ b/answers.md @@ -1,220 +1 @@ Your answers to the questions go here. -# About Datadog -Datadog is a data monitoring service for cloud-scale applications, bringing together data from servers, databases, tools, and services to present a unified view of an entire stack. These capabilities are provided on a SaaS-based data analytics platform. [Wiki](https://en.wikipedia.org/wiki/Datadog) - -### Features -* Observability - From infrastructure to apps, in any environment -* Dashboards - Use instant, real-time boards or build your own -* Infrastructure - From overview to deep details, fast -* Analytics - Custom app metrics or business KPIs -* Collaboration - Share data, discuss in context, solve issues quickly -* Alerts - Avoid alert fatigue with smart, actionable alerts -* API - Love infrastructure as code? You'll love Datadog's API -* Machine Learning - Automatically detect outliers and temporal anomalies -* APM - Monitor, optimize, and troubleshoot app performance - - -_DSC4652 - -I am here to apply for the support engineer at [Datadog](http://datadog.com) Sydney. - - -# The Challenge - -## Questions - -### Level 0 (optional) - Setup an Ubuntu VM - -* While it is not required, we recommend that you spin up a fresh linux VM via Vagrant or other tools so that you don't run into any OS or dependency issues. [Here are instructions for setting up a Vagrant Ubuntu 12.04 VM.](https://www.vagrantup.com/docs/getting-started/) ->Answer: I am using macOS Sierra Version 10.12.6 for this Challenge. - - -### Level 1 - Collecting your Data - -* Sign up for Datadog (use "Datadog Recruiting Candidate" in the "Company" field), get the Agent reporting metrics from your local machine. - ->Answer: [Sign up here](https://www.datadoghq.com/#), get a datadog account for free for 14 days. - ->login, click on [_integration-Agent_](https://app.datadoghq.com/account/settings#agent/mac) in DataDog on the left column and follow the installation instructions for Mac OS X to install the Agent. - -> The datadog can be installed on OS X as easily as: -``` -DD_API_KEY=63ab065b2982aed65fff538ba18a93ba bash -c "$(curl -L https://raw.githubusercontent.com/DataDog/dd-agent/master/packaging/osx/install.sh)" -``` - - -* Bonus question: In your own words, what is the Agent? - ->Answer: The Datadog agent is a full stack data monitoring platform/software which brings data from servers, databases, tools, and services to the Datadog web interface. - -* Add tags in the Agent config file and show us a screenshot of your host and its tags on the Host Map page in Datadog. - ->Answer: Change direction by type _cd ~/.datadog-agent_ in terminal, then type _emacs datadog.conf_ to open and edit the configuration file. -[reference](https://docs.datadoghq.com/guides/tagging/) - - - -After a few minutes refresh the Datadog, go to [_Infrastructure - Host Map_](https://app.datadoghq.com/infrastructure/map?fillby=avg%3Acpuutilization&sizeby=avg%3Anometric&groupby=none&nameby=name&nometrichosts=false&tvMode=false&nogrouphosts=false&palette=green_to_orange&paletteflip=false), the tags are now shown in there. - - - - - -* Install a database on your machine (MongoDB, MySQL, or PostgreSQL) and then install the respective Datadog integration for that database. - ->Answer: I am using PostgreSQL for this part, for how to download and install the software PostgreSQL please refer to -> [Download PostgreSQL here](https://www.postgresql.org/download/) - ->Find the PostgreSQL API under [_Integrations-Integrations_](https://app.datadoghq.com/account/settings), click _install_, then click on _Configuration_ tab. - - ->Create a read-only datadog user with proper access to your PostgreSQL Server. -``` -create user datadog with password 'password_for_datadog'; -grant SELECT ON pg_stat_database to datadog; -``` ->Configure the Agent to connect to the PostgreSQL server ->Edit _conf.d/postgres.yaml_ - - ->Restart the Agent - ->Type _datadog-agent info_ in Terminal to check states. - - ->Can also go to [_Dashboard-Dashboard List_](https://app.datadoghq.com/dash/list) to check whether it is working or not. - -* Write a custom Agent check that samples a random value. Call this new metric: `test.support.random` - ->Answer: Each check will have a configuration file that will be placed in the conf.d directory. Configuration is written using YAML. The file name should match the name of the check module (e.g. randomcheck.py and randomcheck.yaml).There are two places that you will need to add files for your check. -> The first is the checks.d folder, which lives in your Agent root. _~/.datadog-agent/checks.d/randomcheck.py_ - -``` -import random -from checks import AgentCheck -class RandomCheck(AgentCheck): - def check(self, instance): - self.gauge('test.support.random', random.random()) -``` - ->The other folder is conf.d which lives in the Agent configuration root. _~/.datadog-agent/conf.d/randomcheck.yaml_ - -``` -init_config: -instances: - [{}] -``` - -[reference](https://docs.datadoghq.com/guides/agent_checks/) - -Here is a snippet that prints a random value in python: - -```python -import random -print(random.random()) -``` - -### Level 2 - Visualizing your Data - -* Since your database integration is reporting now, clone your database integration dashboard and add additional database metrics to it as well as your `test.support.random` metric from the custom Agent check. - ->Answer: Go to [_Dashboard-Dashboard List_](https://app.datadoghq.com/dash/list), select [_Postgres_](https://app.datadoghq.com/dash/integration/postgresql?live=true&page=0&is_auto=false&from_ts=1508633477863&to_ts=1508637077863&tile_size=m) under _Integration Dashboards_, then click on _Clone Dashboard_ on the top right corner. - - ->Click on add new graph and type _test.support.random_ in the Get then click Save. - - -[reference](https://docs.datadoghq.com/guides/templating/) - -* Bonus question: What is the difference between a timeboard and a screenboard? - ->Answer: all graphs are always scoped to the same time in timeboard, but screenboard is flexible and more customisable, it can be created by drag and drop widgets. - -[reference](https://help.datadoghq.com/hc/en-us/articles/204580349-What-is-the-difference-between-a-ScreenBoard-and-a-TimeBoard-) - -* Take a snapshot of your `test.support.random` graph and draw a box around a section that shows it going above 0.90. Make sure this snapshot is sent to your email by using the @notification - ->Answer: click on the camera icon on the top right of the graph will get you a screenshot of the current graph, draw a box on the graph and type mesage @your_email, then the screenshot will be sent to your email address. - - - -### Level 3 - Alerting on your Data - -Since you've already caught your test metric going above 0.90 once, you don't want to have to continually watch this dashboard to be alerted when it goes above 0.90 again. So let's make life easier by creating a monitor. -* Set up a monitor on this metric that alerts you when it goes above 0.90 at least once during the last 5 minutes - ->Answer: click on the [_setting icon - create Monitor_](https://app.datadoghq.com/monitors#create/metric?aggregator=avg&metric=test.support.random) on the top right of the graph - - - ->1.Choose the detection method - ->2.Select the metric and scope you want to monitor - ->3.Select the alert grouping - - - -[reference](https://docs.datadoghq.com/guides/monitors/) - -* Bonus points: Make it a multi-alert by host so that you won't have to recreate it if your infrastructure scales up. - ->Answer: select _Multi Alert_ under 2-Define the metric, has been done in the previous step. ->A simple alert aggregates over all reporting sources,You will get one alert when the aggregated value meets the conditions set below. This works best to monitor a metric from a single host. A multi alert applies the alert to each source, according to your group parameters. - -* Give it a descriptive monitor name and message (it might be worth it to include the link to your previously created dashboard in the message). Make sure that the monitor will notify you via email. ->Give the monitor a title. It is often useful to use a succinct explanation of the monitor so a notified team member can quickly understand what is going on. - ->Enter a message for the monitor. This field allows standard markdown formatting as well as @your_email_address. - - - - -* This monitor should alert you within 15 minutes. So when it does, take a screenshot of the email that it sends you. - - - -* Bonus: Since this monitor is going to alert pretty often, you don't want to be alerted when you are out of the office. Set up a scheduled downtime for this monitor that silences it from 7pm to 9am daily. Make sure that your email is notified when you schedule the downtime and take a screenshot of that notification. - ->Answer: [_Monitors - Manage Downtime - Schedule Downtime_](https://app.datadoghq.com/monitors#downtime) - ->1.Choose what to silence. - ->2.Set a schedule. - ->(optional)3-4.Add an optional message to notify your team - - - - - -## Instructions -If you have a question, create an issue in this repository. - -To submit your answers: - -1. Fork this repo. -2. Answer the questions in `answers.md` -3. Commit as much code as you need to support your answers. -4. Submit a pull request. -5. Don't forget to include links to your dashboard(s), even better links *and* screenshots. We recommend that you include your screenshots inline with your answers. - -## References - -### How to get started with Datadog - -* [Datadog overview](http://docs.datadoghq.com/overview/) -* [Guide to graphing in Datadog](http://docs.datadoghq.com/graphing/) -* [Guide to monitoring in Datadog](http://docs.datadoghq.com/guides/monitoring/) - -### The Datadog Agent and Metrics - -* [Guide to the Agent](http://docs.datadoghq.com/guides/basic_agent_usage/) -* [Writing an Agent check](http://docs.datadoghq.com/guides/agent_checks/) - -### Other questions: -* [Datadog Help Center](https://help.datadoghq.com/hc/en-us) - -### About Me -Mathematician, Computer Scientist, Barista, World Explorer - -[My Linkedin](https://www.linkedin.com/in/mei-jin/) From 63f0f5e112bdc3ea474408adb91f7df897aa8873 Mon Sep 17 00:00:00 2001 From: Elronia Date: Sun, 28 Nov 2021 16:13:40 -0500 Subject: [PATCH 26/48] Create Table of Contents and Setting Up the Environment --- answers.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/answers.md b/answers.md index 092b6d280..63458f4dd 100644 --- a/answers.md +++ b/answers.md @@ -1 +1,17 @@ Your answers to the questions go here. +## Table of Contents +* [Setting Up the Environment](#Prerequisites - Setting Up the Environment) +* [Collecting Metrics](#Collecting Metrics) +* [Visualizing Data](#Visualizing Data) +* [Monitoring Data](#Monitoring Data) +* [Collecting APM Data](#Collecting APM Data) +* [Final Question](#Final Question) + +## Prerequisites - Setting Up the Environment +1. First, I spinned up a fresh linux Ubuntu VM via Vagrant on Virtual Box on my Mac. I followed these steps: https://medium.com/devops-dudes/how-to-setup-vagrant-and-virtual-box-for-ubuntu-20-04-7374bf9cc3fa +2. Next, I signed up for Datadog (used “Datadog Recruiting Candidate” in the “Company” field). +3. And installed the Datadog Agent on the Vagrant Box by running this command for ubuntu: +```DD_AGENT_MAJOR_VERSION=7 DD_API_KEY=########## DD_SITE="datadoghq.com" bash -c "$(curl -L https://s3.amazonaws.com/dd-agent/scripts/install_script.sh)"``` +I removed the key for security purposes. +4. Finally, got the Agent reporting metrics from my local machine: + From e45884fb35a2b4bd3742d6c687ab13d04e737b11 Mon Sep 17 00:00:00 2001 From: Elronia Date: Sun, 28 Nov 2021 16:17:07 -0500 Subject: [PATCH 27/48] Create Table of Contents and Setting Up the Environment --- answers.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/answers.md b/answers.md index 092b6d280..fe067cb73 100644 --- a/answers.md +++ b/answers.md @@ -1 +1,16 @@ -Your answers to the questions go here. +My answers to the questions go here. +## Table of Contents +* [Setting Up the Environment](#Prerequisites - Setting Up the Environment) +* [Collecting Metrics](#Collecting Metrics) +* [Visualizing Data](#Visualizing Data) +* [Monitoring Data](#Monitoring Data) +* [Collecting APM Data](#Collecting APM Data) +* [Final Question](#Final Question) + +## Prerequisites - Setting Up the Environment +1. First, I spinned up a fresh linux Ubuntu VM via Vagrant on Virtual Box on my Mac. I followed these steps: https://medium.com/devops-dudes/how-to-setup-vagrant-and-virtual-box-for-ubuntu-20-04-7374bf9cc3fa +2. Next, I signed up for Datadog (used “Datadog Recruiting Candidate” in the “Company” field). +3. And installed the Datadog Agent on the Vagrant Box by running this command for ubuntu: +```DD_AGENT_MAJOR_VERSION=7 DD_API_KEY=########## DD_SITE="datadoghq.com" bash -c "$(curl -L https://s3.amazonaws.com/dd-agent/scripts/install_script.sh)"``` +I removed the key for security purposes. +4. Finally, got the Agent reporting metrics from my local machine: \ No newline at end of file From 8bff77e9fa3f556f4c5bec3753b1afc74db29359 Mon Sep 17 00:00:00 2001 From: Elronia Date: Sun, 28 Nov 2021 17:33:23 -0500 Subject: [PATCH 28/48] Add some links --- answers.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/answers.md b/answers.md index 63458f4dd..58affd7ce 100644 --- a/answers.md +++ b/answers.md @@ -1,12 +1,13 @@ Your answers to the questions go here. ## Table of Contents -* [Setting Up the Environment](#Prerequisites - Setting Up the Environment) -* [Collecting Metrics](#Collecting Metrics) -* [Visualizing Data](#Visualizing Data) -* [Monitoring Data](#Monitoring Data) -* [Collecting APM Data](#Collecting APM Data) -* [Final Question](#Final Question) +* [Setting Up the Environment](#setting-up-the-environment) +* [Collecting Metrics](#collecting-metrics) +* [Visualizing Data](#visualizing-data) +* [Monitoring Data](#monitoring-data) +* [Collecting APM Data](#collecting-apm-data) +* [Final Question](#final-question) + ## Prerequisites - Setting Up the Environment 1. First, I spinned up a fresh linux Ubuntu VM via Vagrant on Virtual Box on my Mac. I followed these steps: https://medium.com/devops-dudes/how-to-setup-vagrant-and-virtual-box-for-ubuntu-20-04-7374bf9cc3fa 2. Next, I signed up for Datadog (used “Datadog Recruiting Candidate” in the “Company” field). @@ -15,3 +16,4 @@ Your answers to the questions go here. I removed the key for security purposes. 4. Finally, got the Agent reporting metrics from my local machine: + \ No newline at end of file From eab0c610179d682943f0c60fc04a0f9e3057fc53 Mon Sep 17 00:00:00 2001 From: Elronia Date: Sun, 28 Nov 2021 17:35:10 -0500 Subject: [PATCH 29/48] Add some links --- answers.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/answers.md b/answers.md index 58affd7ce..1c6e03359 100644 --- a/answers.md +++ b/answers.md @@ -8,12 +8,15 @@ Your answers to the questions go here. * [Final Question](#final-question) + ## Prerequisites - Setting Up the Environment 1. First, I spinned up a fresh linux Ubuntu VM via Vagrant on Virtual Box on my Mac. I followed these steps: https://medium.com/devops-dudes/how-to-setup-vagrant-and-virtual-box-for-ubuntu-20-04-7374bf9cc3fa 2. Next, I signed up for Datadog (used “Datadog Recruiting Candidate” in the “Company” field). 3. And installed the Datadog Agent on the Vagrant Box by running this command for ubuntu: -```DD_AGENT_MAJOR_VERSION=7 DD_API_KEY=########## DD_SITE="datadoghq.com" bash -c "$(curl -L https://s3.amazonaws.com/dd-agent/scripts/install_script.sh)"``` +```DD_AGENT_MAJOR_VERSION=7 DD_API_KEY=########## DD_SITE="datadoghq.com" bash -c "$(curl -L https://s3.amazonaws.com/dd-agent/scripts/install_script.sh)"``` I removed the key for security purposes. 4. Finally, got the Agent reporting metrics from my local machine: - \ No newline at end of file + + +## Collecting Metrics \ No newline at end of file From ff1a39e06e37cbe23db3db6fa8e83c023cea2da8 Mon Sep 17 00:00:00 2001 From: Elronia Date: Sun, 28 Nov 2021 18:59:43 -0500 Subject: [PATCH 30/48] Upload screenshots and Flask app --- flask-app/__pycache__/app.cpython-36.pyc | Bin 0 -> 1899 bytes flask-app/app.py | 47 + flask-app/db.sqlite | Bin 0 -> 8192 bytes flask-app/templates/base.html | 44 + flask-app/venv/bin/Activate.ps1 | 231 + flask-app/venv/bin/activate | 76 + flask-app/venv/bin/activate.csh | 37 + flask-app/venv/bin/activate.fish | 75 + flask-app/venv/bin/ddtrace-run | 10 + flask-app/venv/bin/easy_install | 10 + flask-app/venv/bin/easy_install-3.8 | 10 + flask-app/venv/bin/flask | 10 + flask-app/venv/bin/pip | 10 + flask-app/venv/bin/pip3 | 10 + flask-app/venv/bin/pip3.8 | 10 + flask-app/venv/bin/python | 1 + flask-app/venv/bin/python3 | 1 + .../site/python3.8/greenlet/greenlet.h | 146 + .../Flask-2.0.2.dist-info/INSTALLER | 1 + .../Flask-2.0.2.dist-info/LICENSE.rst | 28 + .../Flask-2.0.2.dist-info/METADATA | 125 + .../Flask-2.0.2.dist-info/RECORD | 51 + .../site-packages/Flask-2.0.2.dist-info/WHEEL | 5 + .../Flask-2.0.2.dist-info/entry_points.txt | 3 + .../Flask-2.0.2.dist-info/top_level.txt | 1 + .../INSTALLER | 1 + .../LICENSE.rst | 28 + .../Flask_SQLAlchemy-2.5.1.dist-info/METADATA | 94 + .../Flask_SQLAlchemy-2.5.1.dist-info/RECORD | 14 + .../Flask_SQLAlchemy-2.5.1.dist-info/WHEEL | 6 + .../top_level.txt | 1 + .../Jinja2-3.0.3.dist-info/INSTALLER | 1 + .../Jinja2-3.0.3.dist-info/LICENSE.rst | 28 + .../Jinja2-3.0.3.dist-info/METADATA | 113 + .../Jinja2-3.0.3.dist-info/RECORD | 58 + .../Jinja2-3.0.3.dist-info/WHEEL | 5 + .../Jinja2-3.0.3.dist-info/entry_points.txt | 3 + .../Jinja2-3.0.3.dist-info/top_level.txt | 1 + .../MarkupSafe-2.0.1.dist-info/INSTALLER | 1 + .../MarkupSafe-2.0.1.dist-info/LICENSE.rst | 28 + .../MarkupSafe-2.0.1.dist-info/METADATA | 100 + .../MarkupSafe-2.0.1.dist-info/RECORD | 14 + .../MarkupSafe-2.0.1.dist-info/WHEEL | 5 + .../MarkupSafe-2.0.1.dist-info/top_level.txt | 1 + .../SQLAlchemy-1.4.27.dist-info/INSTALLER | 1 + .../SQLAlchemy-1.4.27.dist-info/LICENSE | 19 + .../SQLAlchemy-1.4.27.dist-info/METADATA | 239 + .../SQLAlchemy-1.4.27.dist-info/RECORD | 485 + .../SQLAlchemy-1.4.27.dist-info/WHEEL | 5 + .../SQLAlchemy-1.4.27.dist-info/top_level.txt | 1 + .../Werkzeug-2.0.2.dist-info/INSTALLER | 1 + .../Werkzeug-2.0.2.dist-info/LICENSE.rst | 28 + .../Werkzeug-2.0.2.dist-info/METADATA | 129 + .../Werkzeug-2.0.2.dist-info/RECORD | 111 + .../Werkzeug-2.0.2.dist-info/WHEEL | 5 + .../Werkzeug-2.0.2.dist-info/top_level.txt | 1 + .../ddtrace_gevent_check.cpython-38.pyc | Bin 0 -> 663 bytes .../__pycache__/easy_install.cpython-38.pyc | Bin 0 -> 337 bytes .../__pycache__/pyparsing.cpython-38.pyc | Bin 0 -> 240862 bytes .../__pycache__/six.cpython-38.pyc | Bin 0 -> 27487 bytes .../python3.8/site-packages/attr/__init__.py | 78 + .../python3.8/site-packages/attr/__init__.pyi | 475 + .../attr/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1834 bytes .../attr/__pycache__/_cmp.cpython-38.pyc | Bin 0 -> 3885 bytes .../attr/__pycache__/_compat.cpython-38.pyc | Bin 0 -> 6116 bytes .../attr/__pycache__/_config.cpython-38.pyc | Bin 0 -> 764 bytes .../attr/__pycache__/_funcs.cpython-38.pyc | Bin 0 -> 10071 bytes .../attr/__pycache__/_make.cpython-38.pyc | Bin 0 -> 72959 bytes .../attr/__pycache__/_next_gen.cpython-38.pyc | Bin 0 -> 3270 bytes .../__pycache__/_version_info.cpython-38.pyc | Bin 0 -> 2443 bytes .../__pycache__/converters.cpython-38.pyc | Bin 0 -> 2921 bytes .../__pycache__/exceptions.cpython-38.pyc | Bin 0 -> 3262 bytes .../attr/__pycache__/filters.cpython-38.pyc | Bin 0 -> 1776 bytes .../attr/__pycache__/setters.cpython-38.pyc | Bin 0 -> 1636 bytes .../__pycache__/validators.cpython-38.pyc | Bin 0 -> 11422 bytes .../lib/python3.8/site-packages/attr/_cmp.py | 152 + .../lib/python3.8/site-packages/attr/_cmp.pyi | 14 + .../python3.8/site-packages/attr/_compat.py | 242 + .../python3.8/site-packages/attr/_config.py | 23 + .../python3.8/site-packages/attr/_funcs.py | 395 + .../lib/python3.8/site-packages/attr/_make.py | 3052 ++++++ .../python3.8/site-packages/attr/_next_gen.py | 158 + .../site-packages/attr/_version_info.py | 85 + .../site-packages/attr/_version_info.pyi | 9 + .../site-packages/attr/converters.py | 111 + .../site-packages/attr/converters.pyi | 13 + .../site-packages/attr/exceptions.py | 92 + .../site-packages/attr/exceptions.pyi | 18 + .../python3.8/site-packages/attr/filters.py | 52 + .../python3.8/site-packages/attr/filters.pyi | 7 + .../lib/python3.8/site-packages/attr/py.typed | 0 .../python3.8/site-packages/attr/setters.py | 77 + .../python3.8/site-packages/attr/setters.pyi | 20 + .../site-packages/attr/validators.py | 379 + .../site-packages/attr/validators.pyi | 68 + .../attrs-21.2.0.dist-info/AUTHORS.rst | 11 + .../attrs-21.2.0.dist-info/INSTALLER | 1 + .../attrs-21.2.0.dist-info/LICENSE | 21 + .../attrs-21.2.0.dist-info/METADATA | 211 + .../attrs-21.2.0.dist-info/RECORD | 42 + .../attrs-21.2.0.dist-info/WHEEL | 6 + .../attrs-21.2.0.dist-info/top_level.txt | 1 + .../site-packages/benchmarks/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 199 bytes .../site-packages/benchmarks/bm/__init__.py | 8 + .../bm/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 303 bytes .../bm/__pycache__/_scenario.cpython-38.pyc | Bin 0 -> 2643 bytes .../site-packages/benchmarks/bm/_scenario.py | 77 + .../click-8.0.3.dist-info/INSTALLER | 1 + .../click-8.0.3.dist-info/LICENSE.rst | 28 + .../click-8.0.3.dist-info/METADATA | 111 + .../click-8.0.3.dist-info/RECORD | 41 + .../site-packages/click-8.0.3.dist-info/WHEEL | 5 + .../click-8.0.3.dist-info/top_level.txt | 1 + .../python3.8/site-packages/click/__init__.py | 75 + .../click/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2710 bytes .../click/__pycache__/_compat.cpython-38.pyc | Bin 0 -> 16123 bytes .../__pycache__/_termui_impl.cpython-38.pyc | Bin 0 -> 15812 bytes .../__pycache__/_textwrap.cpython-38.pyc | Bin 0 -> 1536 bytes .../__pycache__/_unicodefun.cpython-38.pyc | Bin 0 -> 2353 bytes .../__pycache__/_winconsole.cpython-38.pyc | Bin 0 -> 7817 bytes .../click/__pycache__/core.cpython-38.pyc | Bin 0 -> 88247 bytes .../__pycache__/decorators.cpython-38.pyc | Bin 0 -> 14267 bytes .../__pycache__/exceptions.cpython-38.pyc | Bin 0 -> 10167 bytes .../__pycache__/formatting.cpython-38.pyc | Bin 0 -> 9417 bytes .../click/__pycache__/globals.cpython-38.pyc | Bin 0 -> 2451 bytes .../click/__pycache__/parser.cpython-38.pyc | Bin 0 -> 13650 bytes .../shell_completion.cpython-38.pyc | Bin 0 -> 16739 bytes .../click/__pycache__/termui.cpython-38.pyc | Bin 0 -> 26587 bytes .../click/__pycache__/testing.cpython-38.pyc | Bin 0 -> 14873 bytes .../click/__pycache__/types.cpython-38.pyc | Bin 0 -> 33109 bytes .../click/__pycache__/utils.cpython-38.pyc | Bin 0 -> 17803 bytes .../python3.8/site-packages/click/_compat.py | 627 ++ .../site-packages/click/_termui_impl.py | 718 ++ .../site-packages/click/_textwrap.py | 49 + .../site-packages/click/_unicodefun.py | 100 + .../site-packages/click/_winconsole.py | 279 + .../lib/python3.8/site-packages/click/core.py | 2953 ++++++ .../site-packages/click/decorators.py | 436 + .../site-packages/click/exceptions.py | 287 + .../site-packages/click/formatting.py | 301 + .../python3.8/site-packages/click/globals.py | 69 + .../python3.8/site-packages/click/parser.py | 529 ++ .../python3.8/site-packages/click/py.typed | 0 .../site-packages/click/shell_completion.py | 581 ++ .../python3.8/site-packages/click/termui.py | 809 ++ .../python3.8/site-packages/click/testing.py | 479 + .../python3.8/site-packages/click/types.py | 1052 +++ .../python3.8/site-packages/click/utils.py | 579 ++ .../ddtrace-0.55.4.dist-info/INSTALLER | 1 + .../ddtrace-0.55.4.dist-info/LICENSE | 6 + .../ddtrace-0.55.4.dist-info/LICENSE.Apache | 200 + .../ddtrace-0.55.4.dist-info/LICENSE.BSD3 | 24 + .../ddtrace-0.55.4.dist-info/METADATA | 59 + .../ddtrace-0.55.4.dist-info/NOTICE | 4 + .../ddtrace-0.55.4.dist-info/RECORD | 718 ++ .../ddtrace-0.55.4.dist-info/WHEEL | 5 + .../ddtrace-0.55.4.dist-info/entry_points.txt | 9 + .../ddtrace-0.55.4.dist-info/top_level.txt | 3 + .../site-packages/ddtrace/__init__.py | 34 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1037 bytes .../ddtrace/__pycache__/_hooks.cpython-38.pyc | Bin 0 -> 3758 bytes .../__pycache__/_version.cpython-38.pyc | Bin 0 -> 252 bytes .../ddtrace/__pycache__/compat.cpython-38.pyc | Bin 0 -> 1945 bytes .../__pycache__/constants.cpython-38.pyc | Bin 0 -> 1137 bytes .../__pycache__/context.cpython-38.pyc | Bin 0 -> 3540 bytes .../__pycache__/encoding.cpython-38.pyc | Bin 0 -> 638 bytes .../__pycache__/filters.cpython-38.pyc | Bin 0 -> 3077 bytes .../__pycache__/helpers.cpython-38.pyc | Bin 0 -> 1400 bytes .../ddtrace/__pycache__/monkey.cpython-38.pyc | Bin 0 -> 6240 bytes .../ddtrace/__pycache__/pin.cpython-38.pyc | Bin 0 -> 6035 bytes .../__pycache__/provider.cpython-38.pyc | Bin 0 -> 5081 bytes .../__pycache__/sampler.cpython-38.pyc | Bin 0 -> 12897 bytes .../ddtrace/__pycache__/span.cpython-38.pyc | Bin 0 -> 12967 bytes .../ddtrace/__pycache__/tracer.cpython-38.pyc | Bin 0 -> 26914 bytes .../ddtrace/__pycache__/util.cpython-38.pyc | Bin 0 -> 642 bytes .../__pycache__/version.cpython-38.pyc | Bin 0 -> 529 bytes .../python3.8/site-packages/ddtrace/_hooks.py | 133 + .../site-packages/ddtrace/_version.py | 5 + .../ddtrace/bootstrap/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 206 bytes .../__pycache__/sitecustomize.cpython-38.pyc | Bin 0 -> 2977 bytes .../ddtrace/bootstrap/sitecustomize.py | 163 + .../ddtrace/commands/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 205 bytes .../__pycache__/ddtrace_run.cpython-38.pyc | Bin 0 -> 3602 bytes .../ddtrace/commands/ddtrace_run.py | 134 + .../python3.8/site-packages/ddtrace/compat.py | 80 + .../site-packages/ddtrace/constants.py | 38 + .../site-packages/ddtrace/context.py | 135 + .../site-packages/ddtrace/contrib/__init__.py | 3 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 325 bytes .../__pycache__/trace_utils.cpython-38.pyc | Bin 0 -> 9039 bytes .../contrib/__pycache__/util.cpython-38.pyc | Bin 0 -> 541 bytes .../ddtrace/contrib/aiobotocore/__init__.py | 30 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1062 bytes .../__pycache__/patch.cpython-38.pyc | Bin 0 -> 4300 bytes .../ddtrace/contrib/aiobotocore/patch.py | 136 + .../ddtrace/contrib/aiohttp/__init__.py | 65 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2382 bytes .../__pycache__/middlewares.cpython-38.pyc | Bin 0 -> 3603 bytes .../aiohttp/__pycache__/patch.cpython-38.pyc | Bin 0 -> 1453 bytes .../__pycache__/template.cpython-38.pyc | Bin 0 -> 979 bytes .../ddtrace/contrib/aiohttp/middlewares.py | 149 + .../ddtrace/contrib/aiohttp/patch.py | 54 + .../ddtrace/contrib/aiohttp/template.py | 30 + .../ddtrace/contrib/aiopg/__init__.py | 27 + .../aiopg/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 947 bytes .../__pycache__/connection.cpython-38.pyc | Bin 0 -> 3877 bytes .../aiopg/__pycache__/patch.cpython-38.pyc | Bin 0 -> 1927 bytes .../ddtrace/contrib/aiopg/connection.py | 111 + .../ddtrace/contrib/aiopg/patch.py | 57 + .../ddtrace/contrib/algoliasearch/__init__.py | 34 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1094 bytes .../__pycache__/patch.cpython-38.pyc | Bin 0 -> 3618 bytes .../ddtrace/contrib/algoliasearch/patch.py | 152 + .../ddtrace/contrib/aredis/__init__.py | 58 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1501 bytes .../aredis/__pycache__/patch.cpython-38.pyc | Bin 0 -> 4330 bytes .../ddtrace/contrib/aredis/patch.py | 153 + .../ddtrace/contrib/asgi/__init__.py | 75 + .../asgi/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2235 bytes .../__pycache__/middleware.cpython-38.pyc | Bin 0 -> 4919 bytes .../asgi/__pycache__/utils.cpython-38.pyc | Bin 0 -> 3145 bytes .../ddtrace/contrib/asgi/middleware.py | 184 + .../ddtrace/contrib/asgi/utils.py | 82 + .../ddtrace/contrib/asyncio/__init__.py | 62 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2453 bytes .../asyncio/__pycache__/compat.cpython-38.pyc | Bin 0 -> 551 bytes .../__pycache__/helpers.cpython-38.pyc | Bin 0 -> 3179 bytes .../asyncio/__pycache__/patch.cpython-38.pyc | Bin 0 -> 1219 bytes .../__pycache__/provider.cpython-38.pyc | Bin 0 -> 2462 bytes .../__pycache__/wrappers.cpython-38.pyc | Bin 0 -> 1129 bytes .../ddtrace/contrib/asyncio/compat.py | 16 + .../ddtrace/contrib/asyncio/helpers.py | 81 + .../ddtrace/contrib/asyncio/patch.py | 40 + .../ddtrace/contrib/asyncio/provider.py | 74 + .../ddtrace/contrib/asyncio/wrappers.py | 25 + .../ddtrace/contrib/boto/__init__.py | 26 + .../boto/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 865 bytes .../boto/__pycache__/patch.cpython-38.pyc | Bin 0 -> 3658 bytes .../ddtrace/contrib/boto/patch.py | 176 + .../ddtrace/contrib/botocore/__init__.py | 59 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1744 bytes .../botocore/__pycache__/patch.cpython-38.pyc | Bin 0 -> 4802 bytes .../ddtrace/contrib/botocore/patch.py | 177 + .../ddtrace/contrib/bottle/__init__.py | 46 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1254 bytes .../bottle/__pycache__/patch.cpython-38.pyc | Bin 0 -> 1054 bytes .../bottle/__pycache__/trace.cpython-38.pyc | Bin 0 -> 2590 bytes .../ddtrace/contrib/bottle/patch.py | 35 + .../ddtrace/contrib/bottle/trace.py | 95 + .../ddtrace/contrib/cassandra/__init__.py | 37 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1365 bytes .../__pycache__/patch.cpython-38.pyc | Bin 0 -> 307 bytes .../__pycache__/session.cpython-38.pyc | Bin 0 -> 7543 bytes .../ddtrace/contrib/cassandra/patch.py | 5 + .../ddtrace/contrib/cassandra/session.py | 291 + .../ddtrace/contrib/celery/__init__.py | 66 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1780 bytes .../celery/__pycache__/app.cpython-38.pyc | Bin 0 -> 1764 bytes .../__pycache__/constants.cpython-38.pyc | Bin 0 -> 663 bytes .../celery/__pycache__/patch.cpython-38.pyc | Bin 0 -> 1321 bytes .../celery/__pycache__/signals.cpython-38.pyc | Bin 0 -> 4330 bytes .../celery/__pycache__/task.cpython-38.pyc | Bin 0 -> 1129 bytes .../celery/__pycache__/utils.cpython-38.pyc | Bin 0 -> 3801 bytes .../ddtrace/contrib/celery/app.py | 59 + .../ddtrace/contrib/celery/constants.py | 21 + .../ddtrace/contrib/celery/patch.py | 38 + .../ddtrace/contrib/celery/signals.py | 193 + .../ddtrace/contrib/celery/task.py | 31 + .../ddtrace/contrib/celery/utils.py | 126 + .../ddtrace/contrib/cherrypy/__init__.py | 65 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1745 bytes .../__pycache__/middleware.cpython-38.pyc | Bin 0 -> 4234 bytes .../ddtrace/contrib/cherrypy/middleware.py | 146 + .../ddtrace/contrib/consul/__init__.py | 32 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1008 bytes .../consul/__pycache__/patch.cpython-38.pyc | Bin 0 -> 1976 bytes .../ddtrace/contrib/consul/patch.py | 61 + .../ddtrace/contrib/dbapi/__init__.py | 271 + .../dbapi/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 7915 bytes .../ddtrace/contrib/django/__init__.py | 160 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 4503 bytes .../django/__pycache__/_asgi.cpython-38.pyc | Bin 0 -> 1330 bytes .../django/__pycache__/compat.cpython-38.pyc | Bin 0 -> 1010 bytes .../django/__pycache__/patch.cpython-38.pyc | Bin 0 -> 15300 bytes .../__pycache__/restframework.cpython-38.pyc | Bin 0 -> 1433 bytes .../django/__pycache__/utils.cpython-38.pyc | Bin 0 -> 5891 bytes .../ddtrace/contrib/django/_asgi.py | 36 + .../ddtrace/contrib/django/compat.py | 32 + .../ddtrace/contrib/django/patch.py | 541 ++ .../ddtrace/contrib/django/restframework.py | 33 + .../ddtrace/contrib/django/utils.py | 296 + .../ddtrace/contrib/dogpile_cache/__init__.py | 49 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1772 bytes .../__pycache__/lock.cpython-38.pyc | Bin 0 -> 1314 bytes .../__pycache__/patch.cpython-38.pyc | Bin 0 -> 1431 bytes .../__pycache__/region.cpython-38.pyc | Bin 0 -> 1382 bytes .../ddtrace/contrib/dogpile_cache/lock.py | 39 + .../ddtrace/contrib/dogpile_cache/patch.py | 41 + .../ddtrace/contrib/dogpile_cache/region.py | 35 + .../ddtrace/contrib/elasticsearch/__init__.py | 25 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1049 bytes .../__pycache__/patch.cpython-38.pyc | Bin 0 -> 3203 bytes .../__pycache__/quantize.cpython-38.pyc | Bin 0 -> 1139 bytes .../ddtrace/contrib/elasticsearch/patch.py | 120 + .../ddtrace/contrib/elasticsearch/quantize.py | 35 + .../ddtrace/contrib/falcon/__init__.py | 57 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1631 bytes .../__pycache__/middleware.cpython-38.pyc | Bin 0 -> 3181 bytes .../falcon/__pycache__/patch.cpython-38.pyc | Bin 0 -> 1352 bytes .../ddtrace/contrib/falcon/middleware.py | 103 + .../ddtrace/contrib/falcon/patch.py | 46 + .../ddtrace/contrib/fastapi/__init__.py | 66 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1780 bytes .../fastapi/__pycache__/patch.cpython-38.pyc | Bin 0 -> 1776 bytes .../ddtrace/contrib/fastapi/patch.py | 52 + .../ddtrace/contrib/flask/__init__.py | 100 + .../flask/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2367 bytes .../flask/__pycache__/helpers.cpython-38.pyc | Bin 0 -> 1516 bytes .../__pycache__/middleware.cpython-38.pyc | Bin 0 -> 6438 bytes .../flask/__pycache__/patch.cpython-38.pyc | Bin 0 -> 12787 bytes .../flask/__pycache__/wrappers.cpython-38.pyc | Bin 0 -> 1825 bytes .../ddtrace/contrib/flask/helpers.py | 38 + .../ddtrace/contrib/flask/middleware.py | 233 + .../ddtrace/contrib/flask/patch.py | 520 ++ .../ddtrace/contrib/flask/wrappers.py | 48 + .../ddtrace/contrib/flask_cache/__init__.py | 56 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1813 bytes .../__pycache__/tracers.cpython-38.pyc | Bin 0 -> 5439 bytes .../__pycache__/utils.cpython-38.pyc | Bin 0 -> 1559 bytes .../ddtrace/contrib/flask_cache/tracers.py | 156 + .../ddtrace/contrib/flask_cache/utils.py | 57 + .../ddtrace/contrib/futures/__init__.py | 31 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 966 bytes .../futures/__pycache__/patch.cpython-38.pyc | Bin 0 -> 958 bytes .../__pycache__/threading.cpython-38.pyc | Bin 0 -> 1246 bytes .../ddtrace/contrib/futures/patch.py | 24 + .../ddtrace/contrib/futures/threading.py | 41 + .../ddtrace/contrib/gevent/__init__.py | 74 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2223 bytes .../__pycache__/greenlet.cpython-38.pyc | Bin 0 -> 2752 bytes .../gevent/__pycache__/patch.cpython-38.pyc | Bin 0 -> 1951 bytes .../__pycache__/provider.cpython-38.pyc | Bin 0 -> 1859 bytes .../ddtrace/contrib/gevent/greenlet.py | 60 + .../ddtrace/contrib/gevent/patch.py | 67 + .../ddtrace/contrib/gevent/provider.py | 42 + .../ddtrace/contrib/grpc/__init__.py | 88 + .../grpc/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2337 bytes .../client_interceptor.cpython-38.pyc | Bin 0 -> 6788 bytes .../grpc/__pycache__/constants.cpython-38.pyc | Bin 0 -> 1202 bytes .../grpc/__pycache__/patch.cpython-38.pyc | Bin 0 -> 2860 bytes .../server_interceptor.cpython-38.pyc | Bin 0 -> 4475 bytes .../grpc/__pycache__/utils.cpython-38.pyc | Bin 0 -> 1969 bytes .../contrib/grpc/client_interceptor.py | 265 + .../ddtrace/contrib/grpc/constants.py | 24 + .../ddtrace/contrib/grpc/patch.py | 121 + .../contrib/grpc/server_interceptor.py | 124 + .../ddtrace/contrib/grpc/utils.py | 73 + .../ddtrace/contrib/httplib/__init__.py | 65 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1733 bytes .../httplib/__pycache__/patch.cpython-38.pyc | Bin 0 -> 5030 bytes .../ddtrace/contrib/httplib/patch.py | 206 + .../ddtrace/contrib/httpx/__init__.py | 93 + .../httpx/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2641 bytes .../httpx/__pycache__/patch.cpython-38.pyc | Bin 0 -> 3717 bytes .../ddtrace/contrib/httpx/patch.py | 153 + .../ddtrace/contrib/jinja2/__init__.py | 43 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1389 bytes .../__pycache__/constants.cpython-38.pyc | Bin 0 -> 246 bytes .../jinja2/__pycache__/patch.cpython-38.pyc | Bin 0 -> 2855 bytes .../ddtrace/contrib/jinja2/constants.py | 1 + .../ddtrace/contrib/jinja2/patch.py | 99 + .../ddtrace/contrib/kombu/__init__.py | 44 + .../kombu/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1790 bytes .../__pycache__/constants.cpython-38.pyc | Bin 0 -> 236 bytes .../kombu/__pycache__/patch.cpython-38.pyc | Bin 0 -> 3367 bytes .../kombu/__pycache__/utils.cpython-38.pyc | Bin 0 -> 1399 bytes .../ddtrace/contrib/kombu/constants.py | 1 + .../ddtrace/contrib/kombu/patch.py | 128 + .../ddtrace/contrib/kombu/utils.py | 49 + .../ddtrace/contrib/logging/__init__.py | 76 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2341 bytes .../logging/__pycache__/patch.cpython-38.pyc | Bin 0 -> 2928 bytes .../ddtrace/contrib/logging/patch.py | 128 + .../ddtrace/contrib/mako/__init__.py | 26 + .../mako/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 730 bytes .../mako/__pycache__/constants.cpython-38.pyc | Bin 0 -> 244 bytes .../mako/__pycache__/patch.cpython-38.pyc | Bin 0 -> 1870 bytes .../ddtrace/contrib/mako/constants.py | 1 + .../ddtrace/contrib/mako/patch.py | 59 + .../ddtrace/contrib/mariadb/__init__.py | 65 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1810 bytes .../mariadb/__pycache__/patch.cpython-38.pyc | Bin 0 -> 1523 bytes .../ddtrace/contrib/mariadb/patch.py | 49 + .../ddtrace/contrib/molten/__init__.py | 49 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1415 bytes .../molten/__pycache__/patch.cpython-38.pyc | Bin 0 -> 4145 bytes .../__pycache__/wrappers.cpython-38.pyc | Bin 0 -> 3248 bytes .../ddtrace/contrib/molten/patch.py | 164 + .../ddtrace/contrib/molten/wrappers.py | 105 + .../ddtrace/contrib/mongoengine/__init__.py | 30 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1059 bytes .../__pycache__/patch.cpython-38.pyc | Bin 0 -> 843 bytes .../__pycache__/trace.cpython-38.pyc | Bin 0 -> 1265 bytes .../ddtrace/contrib/mongoengine/patch.py | 21 + .../ddtrace/contrib/mongoengine/trace.py | 30 + .../ddtrace/contrib/mysql/__init__.py | 76 + .../mysql/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2198 bytes .../mysql/__pycache__/patch.cpython-38.pyc | Bin 0 -> 1879 bytes .../mysql/__pycache__/tracers.cpython-38.pyc | Bin 0 -> 546 bytes .../ddtrace/contrib/mysql/patch.py | 57 + .../ddtrace/contrib/mysql/tracers.py | 8 + .../ddtrace/contrib/mysqldb/__init__.py | 74 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2078 bytes .../mysqldb/__pycache__/patch.cpython-38.pyc | Bin 0 -> 2066 bytes .../ddtrace/contrib/mysqldb/patch.py | 75 + .../ddtrace/contrib/psycopg/__init__.py | 65 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1773 bytes .../__pycache__/connection.cpython-38.pyc | Bin 0 -> 3418 bytes .../psycopg/__pycache__/patch.cpython-38.pyc | Bin 0 -> 5364 bytes .../ddtrace/contrib/psycopg/connection.py | 97 + .../ddtrace/contrib/psycopg/patch.py | 193 + .../ddtrace/contrib/pylibmc/__init__.py | 32 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1121 bytes .../pylibmc/__pycache__/addrs.cpython-38.pyc | Bin 0 -> 451 bytes .../pylibmc/__pycache__/client.cpython-38.pyc | Bin 0 -> 5603 bytes .../pylibmc/__pycache__/patch.cpython-38.pyc | Bin 0 -> 562 bytes .../ddtrace/contrib/pylibmc/addrs.py | 14 + .../ddtrace/contrib/pylibmc/client.py | 159 + .../ddtrace/contrib/pylibmc/patch.py | 15 + .../ddtrace/contrib/pylons/__init__.py | 71 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1777 bytes .../pylons/__pycache__/compat.cpython-38.pyc | Bin 0 -> 334 bytes .../__pycache__/constants.cpython-38.pyc | Bin 0 -> 254 bytes .../__pycache__/middleware.cpython-38.pyc | Bin 0 -> 3521 bytes .../pylons/__pycache__/patch.cpython-38.pyc | Bin 0 -> 1545 bytes .../__pycache__/renderer.cpython-38.pyc | Bin 0 -> 1308 bytes .../ddtrace/contrib/pylons/compat.py | 8 + .../ddtrace/contrib/pylons/constants.py | 1 + .../ddtrace/contrib/pylons/middleware.py | 124 + .../ddtrace/contrib/pylons/patch.py | 52 + .../ddtrace/contrib/pylons/renderer.py | 37 + .../ddtrace/contrib/pymemcache/__init__.py | 40 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1292 bytes .../__pycache__/client.cpython-38.pyc | Bin 0 -> 7057 bytes .../__pycache__/patch.cpython-38.pyc | Bin 0 -> 1317 bytes .../ddtrace/contrib/pymemcache/client.py | 220 + .../ddtrace/contrib/pymemcache/patch.py | 46 + .../ddtrace/contrib/pymongo/__init__.py | 37 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1396 bytes .../pymongo/__pycache__/client.cpython-38.pyc | Bin 0 -> 7434 bytes .../pymongo/__pycache__/parse.cpython-38.pyc | Bin 0 -> 4130 bytes .../pymongo/__pycache__/patch.cpython-38.pyc | Bin 0 -> 2469 bytes .../ddtrace/contrib/pymongo/client.py | 270 + .../ddtrace/contrib/pymongo/parse.py | 204 + .../ddtrace/contrib/pymongo/patch.py | 84 + .../ddtrace/contrib/pymysql/__init__.py | 68 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1844 bytes .../pymysql/__pycache__/patch.cpython-38.pyc | Bin 0 -> 1695 bytes .../__pycache__/tracers.cpython-38.pyc | Bin 0 -> 553 bytes .../ddtrace/contrib/pymysql/patch.py | 54 + .../ddtrace/contrib/pymysql/tracers.py | 8 + .../ddtrace/contrib/pynamodb/__init__.py | 41 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1121 bytes .../pynamodb/__pycache__/patch.cpython-38.pyc | Bin 0 -> 2382 bytes .../ddtrace/contrib/pynamodb/patch.py | 89 + .../ddtrace/contrib/pyodbc/__init__.py | 65 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1706 bytes .../pyodbc/__pycache__/patch.cpython-38.pyc | Bin 0 -> 1954 bytes .../ddtrace/contrib/pyodbc/patch.py | 55 + .../ddtrace/contrib/pyramid/__init__.py | 60 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1798 bytes .../__pycache__/constants.cpython-38.pyc | Bin 0 -> 542 bytes .../pyramid/__pycache__/patch.cpython-38.pyc | Bin 0 -> 2243 bytes .../pyramid/__pycache__/trace.cpython-38.pyc | Bin 0 -> 3391 bytes .../ddtrace/contrib/pyramid/constants.py | 6 + .../ddtrace/contrib/pyramid/patch.py | 93 + .../ddtrace/contrib/pyramid/trace.py | 123 + .../ddtrace/contrib/pytest/__init__.py | 54 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1564 bytes .../__pycache__/constants.cpython-38.pyc | Bin 0 -> 303 bytes .../pytest/__pycache__/plugin.cpython-38.pyc | Bin 0 -> 6227 bytes .../ddtrace/contrib/pytest/constants.py | 4 + .../ddtrace/contrib/pytest/plugin.py | 204 + .../ddtrace/contrib/redis/__init__.py | 59 + .../redis/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1559 bytes .../redis/__pycache__/patch.cpython-38.pyc | Bin 0 -> 3765 bytes .../redis/__pycache__/tracers.cpython-38.pyc | Bin 0 -> 857 bytes .../redis/__pycache__/util.cpython-38.pyc | Bin 0 -> 1419 bytes .../ddtrace/contrib/redis/patch.py | 123 + .../ddtrace/contrib/redis/tracers.py | 20 + .../ddtrace/contrib/redis/util.py | 54 + .../ddtrace/contrib/rediscluster/__init__.py | 29 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1035 bytes .../__pycache__/patch.cpython-38.pyc | Bin 0 -> 2667 bytes .../ddtrace/contrib/rediscluster/patch.py | 77 + .../ddtrace/contrib/requests/__init__.py | 89 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2328 bytes .../__pycache__/connection.cpython-38.pyc | Bin 0 -> 2566 bytes .../__pycache__/constants.cpython-38.pyc | Bin 0 -> 242 bytes .../__pycache__/legacy.cpython-38.pyc | Bin 0 -> 1080 bytes .../requests/__pycache__/patch.cpython-38.pyc | Bin 0 -> 1383 bytes .../__pycache__/session.cpython-38.pyc | Bin 0 -> 810 bytes .../ddtrace/contrib/requests/connection.py | 125 + .../ddtrace/contrib/requests/constants.py | 1 + .../ddtrace/contrib/requests/legacy.py | 31 + .../ddtrace/contrib/requests/patch.py | 49 + .../ddtrace/contrib/requests/session.py | 21 + .../ddtrace/contrib/rq/__init__.py | 251 + .../rq/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 6003 bytes .../ddtrace/contrib/sanic/__init__.py | 75 + .../sanic/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1943 bytes .../sanic/__pycache__/patch.cpython-38.pyc | Bin 0 -> 4815 bytes .../ddtrace/contrib/sanic/patch.py | 177 + .../ddtrace/contrib/snowflake/__init__.py | 71 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2012 bytes .../__pycache__/patch.cpython-38.pyc | Bin 0 -> 1724 bytes .../ddtrace/contrib/snowflake/patch.py | 62 + .../ddtrace/contrib/sqlalchemy/__init__.py | 29 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1078 bytes .../__pycache__/engine.cpython-38.pyc | Bin 0 -> 4468 bytes .../__pycache__/patch.cpython-38.pyc | Bin 0 -> 857 bytes .../ddtrace/contrib/sqlalchemy/engine.py | 153 + .../ddtrace/contrib/sqlalchemy/patch.py | 24 + .../ddtrace/contrib/sqlite3/__init__.py | 60 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1627 bytes .../__pycache__/connection.cpython-38.pyc | Bin 0 -> 516 bytes .../sqlite3/__pycache__/patch.cpython-38.pyc | Bin 0 -> 2625 bytes .../ddtrace/contrib/sqlite3/connection.py | 8 + .../ddtrace/contrib/sqlite3/patch.py | 78 + .../ddtrace/contrib/starlette/__init__.py | 86 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2417 bytes .../__pycache__/patch.cpython-38.pyc | Bin 0 -> 2052 bytes .../ddtrace/contrib/starlette/patch.py | 67 + .../ddtrace/contrib/tornado/__init__.py | 128 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 4562 bytes .../__pycache__/application.cpython-38.pyc | Bin 0 -> 1299 bytes .../tornado/__pycache__/compat.cpython-38.pyc | Bin 0 -> 571 bytes .../__pycache__/constants.cpython-38.pyc | Bin 0 -> 435 bytes .../__pycache__/decorators.cpython-38.pyc | Bin 0 -> 1816 bytes .../__pycache__/handlers.cpython-38.pyc | Bin 0 -> 3078 bytes .../tornado/__pycache__/patch.cpython-38.pyc | Bin 0 -> 1928 bytes .../__pycache__/stack_context.cpython-38.pyc | Bin 0 -> 5271 bytes .../__pycache__/template.cpython-38.pyc | Bin 0 -> 1049 bytes .../ddtrace/contrib/tornado/application.py | 58 + .../ddtrace/contrib/tornado/compat.py | 16 + .../ddtrace/contrib/tornado/constants.py | 7 + .../ddtrace/contrib/tornado/decorators.py | 77 + .../ddtrace/contrib/tornado/handlers.py | 113 + .../ddtrace/contrib/tornado/patch.py | 73 + .../ddtrace/contrib/tornado/stack_context.py | 147 + .../ddtrace/contrib/tornado/template.py | 31 + .../ddtrace/contrib/trace_utils.py | 323 + .../ddtrace/contrib/urllib3/__init__.py | 66 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1730 bytes .../urllib3/__pycache__/patch.cpython-38.pyc | Bin 0 -> 3360 bytes .../ddtrace/contrib/urllib3/patch.py | 129 + .../site-packages/ddtrace/contrib/util.py | 18 + .../ddtrace/contrib/vertica/__init__.py | 52 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1522 bytes .../__pycache__/constants.cpython-38.pyc | Bin 0 -> 228 bytes .../vertica/__pycache__/patch.cpython-38.pyc | Bin 0 -> 5569 bytes .../ddtrace/contrib/vertica/constants.py | 2 + .../ddtrace/contrib/vertica/patch.py | 243 + .../ddtrace/contrib/wsgi/__init__.py | 44 + .../wsgi/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1046 bytes .../wsgi/__pycache__/wsgi.cpython-38.pyc | Bin 0 -> 4312 bytes .../ddtrace/contrib/wsgi/wsgi.py | 152 + .../site-packages/ddtrace/encoding.py | 20 + .../site-packages/ddtrace/ext/__init__.py | 16 + .../ext/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 651 bytes .../ext/__pycache__/aws.cpython-38.pyc | Bin 0 -> 1647 bytes .../ext/__pycache__/cassandra.cpython-38.pyc | Bin 0 -> 438 bytes .../ddtrace/ext/__pycache__/ci.cpython-38.pyc | Bin 0 -> 12591 bytes .../ext/__pycache__/consul.cpython-38.pyc | Bin 0 -> 277 bytes .../ddtrace/ext/__pycache__/db.cpython-38.pyc | Bin 0 -> 260 bytes .../__pycache__/elasticsearch.cpython-38.pyc | Bin 0 -> 407 bytes .../ext/__pycache__/errors.cpython-38.pyc | Bin 0 -> 1044 bytes .../ext/__pycache__/git.cpython-38.pyc | Bin 0 -> 4851 bytes .../ext/__pycache__/http.cpython-38.pyc | Bin 0 -> 564 bytes .../ext/__pycache__/kombu.cpython-38.pyc | Bin 0 -> 413 bytes .../ext/__pycache__/memcached.cpython-38.pyc | Bin 0 -> 282 bytes .../ext/__pycache__/mongo.cpython-38.pyc | Bin 0 -> 330 bytes .../ext/__pycache__/net.cpython-38.pyc | Bin 0 -> 321 bytes .../ext/__pycache__/priority.cpython-38.pyc | Bin 0 -> 1054 bytes .../ext/__pycache__/redis.cpython-38.pyc | Bin 0 -> 434 bytes .../ext/__pycache__/sql.cpython-38.pyc | Bin 0 -> 1144 bytes .../ext/__pycache__/system.cpython-38.pyc | Bin 0 -> 482 bytes .../ext/__pycache__/test.cpython-38.pyc | Bin 0 -> 1118 bytes .../site-packages/ddtrace/ext/aws.py | 51 + .../site-packages/ddtrace/ext/cassandra.py | 7 + .../python3.8/site-packages/ddtrace/ext/ci.py | 446 + .../site-packages/ddtrace/ext/consul.py | 4 + .../python3.8/site-packages/ddtrace/ext/db.py | 4 + .../ddtrace/ext/elasticsearch.py | 9 + .../site-packages/ddtrace/ext/errors.py | 37 + .../site-packages/ddtrace/ext/git.py | 161 + .../site-packages/ddtrace/ext/http.py | 19 + .../site-packages/ddtrace/ext/kombu.py | 12 + .../site-packages/ddtrace/ext/memcached.py | 3 + .../site-packages/ddtrace/ext/mongo.py | 5 + .../site-packages/ddtrace/ext/net.py | 9 + .../site-packages/ddtrace/ext/priority.py | 29 + .../site-packages/ddtrace/ext/redis.py | 13 + .../site-packages/ddtrace/ext/sql.py | 34 + .../site-packages/ddtrace/ext/system.py | 14 + .../site-packages/ddtrace/ext/test.py | 50 + .../site-packages/ddtrace/filters.py | 72 + .../site-packages/ddtrace/helpers.py | 48 + .../site-packages/ddtrace/http/__init__.py | 16 + .../http/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 535 bytes .../http/__pycache__/headers.cpython-38.pyc | Bin 0 -> 864 bytes .../site-packages/ddtrace/http/headers.py | 27 + .../ddtrace/internal/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 205 bytes .../internal/__pycache__/agent.cpython-38.pyc | Bin 0 -> 3715 bytes .../__pycache__/atexit.cpython-38.pyc | Bin 0 -> 1495 bytes .../__pycache__/compat.cpython-38.pyc | Bin 0 -> 5087 bytes .../internal/__pycache__/debug.cpython-38.pyc | Bin 0 -> 7303 bytes .../__pycache__/dogstatsd.cpython-38.pyc | Bin 0 -> 870 bytes .../__pycache__/encoding.cpython-38.pyc | Bin 0 -> 3811 bytes .../__pycache__/forksafe.cpython-38.pyc | Bin 0 -> 3421 bytes .../__pycache__/hostname.cpython-38.pyc | Bin 0 -> 422 bytes .../internal/__pycache__/http.cpython-38.pyc | Bin 0 -> 1555 bytes .../__pycache__/logger.cpython-38.pyc | Bin 0 -> 4243 bytes .../__pycache__/nogevent.cpython-38.pyc | Bin 0 -> 2287 bytes .../__pycache__/periodic.cpython-38.pyc | Bin 0 -> 6185 bytes .../__pycache__/rate_limiter.cpython-38.pyc | Bin 0 -> 3441 bytes .../__pycache__/service.cpython-38.pyc | Bin 0 -> 3033 bytes .../internal/__pycache__/sma.cpython-38.pyc | Bin 0 -> 1759 bytes .../internal/__pycache__/uds.cpython-38.pyc | Bin 0 -> 1030 bytes .../internal/__pycache__/uwsgi.cpython-38.pyc | Bin 0 -> 1853 bytes .../__pycache__/writer.cpython-38.pyc | Bin 0 -> 14189 bytes .../internal/_encoding.cpython-38-darwin.so | Bin 0 -> 227536 bytes .../internal/_rand.cpython-38-darwin.so | Bin 0 -> 61128 bytes .../site-packages/ddtrace/internal/agent.py | 130 + .../site-packages/ddtrace/internal/atexit.py | 58 + .../site-packages/ddtrace/internal/compat.py | 253 + .../site-packages/ddtrace/internal/debug.py | 247 + .../ddtrace/internal/dogstatsd.py | 27 + .../ddtrace/internal/encoding.py | 96 + .../ddtrace/internal/forksafe.py | 137 + .../ddtrace/internal/hostname.py | 13 + .../site-packages/ddtrace/internal/http.py | 36 + .../site-packages/ddtrace/internal/logger.py | 176 + .../ddtrace/internal/nogevent.py | 84 + .../ddtrace/internal/periodic.py | 244 + .../ddtrace/internal/processor/__init__.py | 53 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2045 bytes .../__pycache__/trace.cpython-38.pyc | Bin 0 -> 5626 bytes .../ddtrace/internal/processor/trace.py | 166 + .../ddtrace/internal/rate_limiter.py | 158 + .../ddtrace/internal/runtime/__init__.py | 29 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 835 bytes .../__pycache__/collector.cpython-38.pyc | Bin 0 -> 2817 bytes .../__pycache__/constants.cpython-38.pyc | Bin 0 -> 1131 bytes .../__pycache__/container.cpython-38.pyc | Bin 0 -> 3272 bytes .../metric_collectors.cpython-38.pyc | Bin 0 -> 3096 bytes .../runtime_metrics.cpython-38.pyc | Bin 0 -> 5707 bytes .../__pycache__/tag_collectors.cpython-38.pyc | Bin 0 -> 2249 bytes .../ddtrace/internal/runtime/collector.py | 89 + .../ddtrace/internal/runtime/constants.py | 33 + .../ddtrace/internal/runtime/container.py | 111 + .../internal/runtime/metric_collectors.py | 94 + .../internal/runtime/runtime_metrics.py | 165 + .../internal/runtime/tag_collectors.py | 59 + .../site-packages/ddtrace/internal/service.py | 106 + .../site-packages/ddtrace/internal/sma.py | 67 + .../site-packages/ddtrace/internal/uds.py | 27 + .../site-packages/ddtrace/internal/uwsgi.py | 70 + .../site-packages/ddtrace/internal/writer.py | 493 + .../python3.8/site-packages/ddtrace/monkey.py | 281 + .../ddtrace/opentracer/__init__.py | 8 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 326 bytes .../__pycache__/helpers.cpython-38.pyc | Bin 0 -> 550 bytes .../__pycache__/settings.cpython-38.pyc | Bin 0 -> 1131 bytes .../__pycache__/span.cpython-38.pyc | Bin 0 -> 5636 bytes .../__pycache__/span_context.cpython-38.pyc | Bin 0 -> 1984 bytes .../__pycache__/tags.cpython-38.pyc | Bin 0 -> 544 bytes .../__pycache__/tracer.cpython-38.pyc | Bin 0 -> 11286 bytes .../__pycache__/utils.cpython-38.pyc | Bin 0 -> 1815 bytes .../ddtrace/opentracer/helpers.py | 25 + .../opentracer/propagation/__init__.py | 6 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 286 bytes .../__pycache__/binary.cpython-38.pyc | Bin 0 -> 217 bytes .../__pycache__/http.cpython-38.pyc | Bin 0 -> 2661 bytes .../__pycache__/propagator.cpython-38.pyc | Bin 0 -> 760 bytes .../__pycache__/text.cpython-38.pyc | Bin 0 -> 215 bytes .../ddtrace/opentracer/propagation/binary.py | 0 .../ddtrace/opentracer/propagation/http.py | 82 + .../opentracer/propagation/propagator.py | 15 + .../ddtrace/opentracer/propagation/text.py | 0 .../ddtrace/opentracer/settings.py | 41 + .../site-packages/ddtrace/opentracer/span.py | 194 + .../ddtrace/opentracer/span_context.py | 66 + .../site-packages/ddtrace/opentracer/tags.py | 23 + .../ddtrace/opentracer/tracer.py | 375 + .../site-packages/ddtrace/opentracer/utils.py | 60 + .../python3.8/site-packages/ddtrace/pin.py | 219 + .../ddtrace/profiling/__init__.py | 21 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 805 bytes .../__pycache__/_traceback.cpython-38.pyc | Bin 0 -> 404 bytes .../profiling/__pycache__/auto.cpython-38.pyc | Bin 0 -> 355 bytes .../__pycache__/event.cpython-38.pyc | Bin 0 -> 2212 bytes .../__pycache__/profiler.cpython-38.pyc | Bin 0 -> 8186 bytes .../__pycache__/recorder.cpython-38.pyc | Bin 0 -> 3421 bytes .../__pycache__/scheduler.cpython-38.pyc | Bin 0 -> 2491 bytes .../profiling/_build.cpython-38-darwin.so | Bin 0 -> 39968 bytes .../ddtrace/profiling/_traceback.py | 5 + .../site-packages/ddtrace/profiling/auto.py | 5 + .../ddtrace/profiling/bootstrap/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 216 bytes .../__pycache__/sitecustomize.cpython-38.pyc | Bin 0 -> 594 bytes .../profiling/bootstrap/sitecustomize.py | 15 + .../ddtrace/profiling/collector/__init__.py | 81 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 3230 bytes .../__pycache__/memalloc.cpython-38.pyc | Bin 0 -> 4690 bytes .../__pycache__/threading.cpython-38.pyc | Bin 0 -> 5900 bytes .../collector/_memalloc.cpython-38-darwin.so | Bin 0 -> 40432 bytes .../collector/_task.cpython-38-darwin.so | Bin 0 -> 73664 bytes .../collector/_threading.cpython-38-darwin.so | Bin 0 -> 45056 bytes .../collector/_traceback.cpython-38-darwin.so | Bin 0 -> 43288 bytes .../ddtrace/profiling/collector/memalloc.py | 166 + .../collector/stack.cpython-38-darwin.so | Bin 0 -> 271240 bytes .../ddtrace/profiling/collector/threading.py | 194 + .../site-packages/ddtrace/profiling/event.py | 64 + .../ddtrace/profiling/exporter/__init__.py | 30 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1250 bytes .../exporter/__pycache__/file.cpython-38.pyc | Bin 0 -> 1405 bytes .../exporter/__pycache__/http.cpython-38.pyc | Bin 0 -> 6687 bytes .../__pycache__/pprof_pb2.cpython-38.pyc | Bin 0 -> 10239 bytes .../pprof_pre312_pb2.cpython-38.pyc | Bin 0 -> 9991 bytes .../ddtrace/profiling/exporter/file.py | 30 + .../ddtrace/profiling/exporter/http.py | 204 + .../exporter/pprof.cpython-38-darwin.so | Bin 0 -> 457080 bytes .../ddtrace/profiling/exporter/pprof_pb2.py | 621 ++ .../profiling/exporter/pprof_pre312_pb2.py | 615 ++ .../ddtrace/profiling/profiler.py | 281 + .../ddtrace/profiling/recorder.py | 90 + .../ddtrace/profiling/scheduler.py | 67 + .../ddtrace/propagation/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 208 bytes .../__pycache__/http.cpython-38.pyc | Bin 0 -> 3995 bytes .../__pycache__/utils.cpython-38.pyc | Bin 0 -> 1092 bytes .../site-packages/ddtrace/propagation/http.py | 147 + .../ddtrace/propagation/utils.py | 31 + .../site-packages/ddtrace/provider.py | 138 + .../python3.8/site-packages/ddtrace/py.typed | 0 .../site-packages/ddtrace/runtime/__init__.py | 60 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2240 bytes .../site-packages/ddtrace/sampler.py | 443 + .../ddtrace/settings/__init__.py | 17 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 502 bytes .../__pycache__/config.cpython-38.pyc | Bin 0 -> 7859 bytes .../__pycache__/exceptions.cpython-38.pyc | Bin 0 -> 502 bytes .../settings/__pycache__/http.cpython-38.pyc | Bin 0 -> 2896 bytes .../__pycache__/integration.cpython-38.pyc | Bin 0 -> 4206 bytes .../site-packages/ddtrace/settings/config.py | 244 + .../ddtrace/settings/exceptions.py | 6 + .../site-packages/ddtrace/settings/http.py | 83 + .../ddtrace/settings/integration.py | 135 + .../python3.8/site-packages/ddtrace/span.py | 538 ++ .../python3.8/site-packages/ddtrace/tracer.py | 1024 ++ .../python3.8/site-packages/ddtrace/util.py | 24 + .../site-packages/ddtrace/utils/__init__.py | 41 + .../utils/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1724 bytes .../utils/__pycache__/attr.cpython-38.pyc | Bin 0 -> 643 bytes .../utils/__pycache__/attrdict.cpython-38.pyc | Bin 0 -> 1134 bytes .../utils/__pycache__/cache.cpython-38.pyc | Bin 0 -> 2658 bytes .../utils/__pycache__/config.cpython-38.pyc | Bin 0 -> 640 bytes .../__pycache__/deprecation.cpython-38.pyc | Bin 0 -> 3820 bytes .../utils/__pycache__/formats.cpython-38.pyc | Bin 0 -> 4034 bytes .../utils/__pycache__/http.cpython-38.pyc | Bin 0 -> 996 bytes .../__pycache__/importlib.cpython-38.pyc | Bin 0 -> 1740 bytes .../utils/__pycache__/time.cpython-38.pyc | Bin 0 -> 2363 bytes .../utils/__pycache__/version.cpython-38.pyc | Bin 0 -> 935 bytes .../utils/__pycache__/wrappers.cpython-38.pyc | Bin 0 -> 2554 bytes .../site-packages/ddtrace/utils/attr.py | 16 + .../site-packages/ddtrace/utils/attrdict.py | 42 + .../site-packages/ddtrace/utils/cache.py | 85 + .../site-packages/ddtrace/utils/config.py | 19 + .../ddtrace/utils/deprecation.py | 101 + .../site-packages/ddtrace/utils/formats.py | 154 + .../site-packages/ddtrace/utils/http.py | 27 + .../site-packages/ddtrace/utils/importlib.py | 44 + .../site-packages/ddtrace/utils/time.py | 67 + .../site-packages/ddtrace/utils/version.py | 44 + .../site-packages/ddtrace/utils/wrappers.py | 91 + .../site-packages/ddtrace/vendor/__init__.py | 97 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2428 bytes .../ddtrace/vendor/contextvars/__init__.py | 160 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 4800 bytes .../ddtrace/vendor/debtcollector/__init__.py | 45 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1739 bytes .../__pycache__/_utils.cpython-38.pyc | Bin 0 -> 4246 bytes .../__pycache__/moves.cpython-38.pyc | Bin 0 -> 6572 bytes .../__pycache__/removals.cpython-38.pyc | Bin 0 -> 10679 bytes .../__pycache__/renames.cpython-38.pyc | Bin 0 -> 1179 bytes .../__pycache__/updating.cpython-38.pyc | Bin 0 -> 1738 bytes .../ddtrace/vendor/debtcollector/_utils.py | 180 + .../ddtrace/vendor/debtcollector/moves.py | 197 + .../ddtrace/vendor/debtcollector/removals.py | 334 + .../ddtrace/vendor/debtcollector/renames.py | 45 + .../ddtrace/vendor/debtcollector/updating.py | 68 + .../ddtrace/vendor/dogstatsd/__init__.py | 4 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 268 bytes .../dogstatsd/__pycache__/base.cpython-38.pyc | Bin 0 -> 18968 bytes .../__pycache__/compat.cpython-38.pyc | Bin 0 -> 644 bytes .../__pycache__/context.cpython-38.pyc | Bin 0 -> 3218 bytes .../__pycache__/context_async.cpython-38.pyc | Bin 0 -> 1067 bytes .../__pycache__/format.cpython-38.pyc | Bin 0 -> 1372 bytes .../__pycache__/route.cpython-38.pyc | Bin 0 -> 1392 bytes .../ddtrace/vendor/dogstatsd/base.py | 639 ++ .../ddtrace/vendor/dogstatsd/compat.py | 33 + .../ddtrace/vendor/dogstatsd/context.py | 81 + .../ddtrace/vendor/dogstatsd/context_async.py | 50 + .../ddtrace/vendor/dogstatsd/format.py | 33 + .../ddtrace/vendor/dogstatsd/route.py | 41 + .../ddtrace/vendor/monotonic/__init__.py | 169 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 4539 bytes .../ddtrace/vendor/psutil/__init__.py | 2516 +++++ .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 67239 bytes .../psutil/__pycache__/_common.cpython-38.pyc | Bin 0 -> 15619 bytes .../psutil/__pycache__/_compat.cpython-38.pyc | Bin 0 -> 9928 bytes .../psutil/__pycache__/_psaix.cpython-38.pyc | Bin 0 -> 15280 bytes .../psutil/__pycache__/_psbsd.cpython-38.pyc | Bin 0 -> 20598 bytes .../__pycache__/_pslinux.cpython-38.pyc | Bin 0 -> 48430 bytes .../psutil/__pycache__/_psosx.cpython-38.pyc | Bin 0 -> 14591 bytes .../__pycache__/_psposix.cpython-38.pyc | Bin 0 -> 3748 bytes .../__pycache__/_pssunos.cpython-38.pyc | Bin 0 -> 18188 bytes .../__pycache__/_pswindows.cpython-38.pyc | Bin 0 -> 29268 bytes .../psutil/__pycache__/setup.cpython-38.pyc | Bin 0 -> 5807 bytes .../ddtrace/vendor/psutil/_common.py | 651 ++ .../ddtrace/vendor/psutil/_compat.py | 332 + .../ddtrace/vendor/psutil/_psaix.py | 554 ++ .../ddtrace/vendor/psutil/_psbsd.py | 905 ++ .../ddtrace/vendor/psutil/_pslinux.py | 2096 +++++ .../ddtrace/vendor/psutil/_psosx.py | 568 ++ .../ddtrace/vendor/psutil/_psposix.py | 179 + .../ddtrace/vendor/psutil/_pssunos.py | 720 ++ .../psutil/_psutil_osx.cpython-38-darwin.so | Bin 0 -> 65232 bytes .../psutil/_psutil_posix.cpython-38-darwin.so | Bin 0 -> 65232 bytes .../ddtrace/vendor/psutil/_pswindows.py | 1127 +++ .../ddtrace/vendor/psutil/setup.py | 227 + .../ddtrace/vendor/wrapt/__init__.py | 16 + .../wrapt/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1021 bytes .../__pycache__/decorators.cpython-38.pyc | Bin 0 -> 9293 bytes .../wrapt/__pycache__/importer.cpython-38.pyc | Bin 0 -> 4301 bytes .../wrapt/__pycache__/setup.cpython-38.pyc | Bin 0 -> 465 bytes .../wrapt/__pycache__/wrappers.cpython-38.pyc | Bin 0 -> 24426 bytes .../wrapt/_wrappers.cpython-38-darwin.so | Bin 0 -> 74152 bytes .../ddtrace/vendor/wrapt/decorators.py | 516 ++ .../ddtrace/vendor/wrapt/importer.py | 230 + .../ddtrace/vendor/wrapt/setup.py | 7 + .../ddtrace/vendor/wrapt/wrappers.py | 947 ++ .../site-packages/ddtrace/version.py | 16 + .../site-packages/ddtrace_gevent_check.py | 13 + .../python3.8/site-packages/easy_install.py | 5 + .../python3.8/site-packages/flask/__init__.py | 46 + .../python3.8/site-packages/flask/__main__.py | 3 + .../flask/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1901 bytes .../flask/__pycache__/__main__.cpython-38.pyc | Bin 0 -> 232 bytes .../flask/__pycache__/app.cpython-38.pyc | Bin 0 -> 63153 bytes .../__pycache__/blueprints.cpython-38.pyc | Bin 0 -> 21959 bytes .../flask/__pycache__/cli.cpython-38.pyc | Bin 0 -> 27145 bytes .../flask/__pycache__/config.cpython-38.pyc | Bin 0 -> 11686 bytes .../flask/__pycache__/ctx.cpython-38.pyc | Bin 0 -> 15466 bytes .../__pycache__/debughelpers.cpython-38.pyc | Bin 0 -> 6486 bytes .../flask/__pycache__/globals.cpython-38.pyc | Bin 0 -> 1853 bytes .../flask/__pycache__/helpers.cpython-38.pyc | Bin 0 -> 27219 bytes .../flask/__pycache__/logging.cpython-38.pyc | Bin 0 -> 2472 bytes .../flask/__pycache__/scaffold.cpython-38.pyc | Bin 0 -> 24900 bytes .../flask/__pycache__/sessions.cpython-38.pyc | Bin 0 -> 13137 bytes .../flask/__pycache__/signals.cpython-38.pyc | Bin 0 -> 2390 bytes .../__pycache__/templating.cpython-38.pyc | Bin 0 -> 5577 bytes .../flask/__pycache__/testing.cpython-38.pyc | Bin 0 -> 8986 bytes .../flask/__pycache__/typing.cpython-38.pyc | Bin 0 -> 1793 bytes .../flask/__pycache__/views.cpython-38.pyc | Bin 0 -> 5018 bytes .../flask/__pycache__/wrappers.cpython-38.pyc | Bin 0 -> 5041 bytes .../lib/python3.8/site-packages/flask/app.py | 2091 +++++ .../site-packages/flask/blueprints.py | 609 ++ .../lib/python3.8/site-packages/flask/cli.py | 998 ++ .../python3.8/site-packages/flask/config.py | 295 + .../lib/python3.8/site-packages/flask/ctx.py | 480 + .../site-packages/flask/debughelpers.py | 172 + .../python3.8/site-packages/flask/globals.py | 59 + .../python3.8/site-packages/flask/helpers.py | 836 ++ .../site-packages/flask/json/__init__.py | 357 + .../json/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 11595 bytes .../flask/json/__pycache__/tag.cpython-38.pyc | Bin 0 -> 11497 bytes .../python3.8/site-packages/flask/json/tag.py | 312 + .../python3.8/site-packages/flask/logging.py | 74 + .../python3.8/site-packages/flask/py.typed | 0 .../python3.8/site-packages/flask/scaffold.py | 875 ++ .../python3.8/site-packages/flask/sessions.py | 404 + .../python3.8/site-packages/flask/signals.py | 56 + .../site-packages/flask/templating.py | 165 + .../python3.8/site-packages/flask/testing.py | 280 + .../python3.8/site-packages/flask/typing.py | 56 + .../python3.8/site-packages/flask/views.py | 158 + .../python3.8/site-packages/flask/wrappers.py | 167 + .../flask_sqlalchemy/__init__.py | 1132 +++ .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 36997 bytes .../__pycache__/_compat.cpython-38.pyc | Bin 0 -> 1206 bytes .../__pycache__/model.cpython-38.pyc | Bin 0 -> 4725 bytes .../__pycache__/utils.cpython-38.pyc | Bin 0 -> 1513 bytes .../site-packages/flask_sqlalchemy/_compat.py | 44 + .../site-packages/flask_sqlalchemy/model.py | 154 + .../site-packages/flask_sqlalchemy/utils.py | 45 + .../site-packages/google/protobuf/__init__.py | 33 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 227 bytes .../__pycache__/any_pb2.cpython-38.pyc | Bin 0 -> 1265 bytes .../__pycache__/api_pb2.cpython-38.pyc | Bin 0 -> 2165 bytes .../__pycache__/descriptor.cpython-38.pyc | Bin 0 -> 36015 bytes .../descriptor_database.cpython-38.pyc | Bin 0 -> 5021 bytes .../__pycache__/descriptor_pb2.cpython-38.pyc | Bin 0 -> 36363 bytes .../descriptor_pool.cpython-38.pyc | Bin 0 -> 33942 bytes .../__pycache__/duration_pb2.cpython-38.pyc | Bin 0 -> 1321 bytes .../__pycache__/empty_pb2.cpython-38.pyc | Bin 0 -> 1258 bytes .../__pycache__/field_mask_pb2.cpython-38.pyc | Bin 0 -> 1317 bytes .../__pycache__/json_format.cpython-38.pyc | Bin 0 -> 23030 bytes .../__pycache__/message.cpython-38.pyc | Bin 0 -> 13005 bytes .../message_factory.cpython-38.pyc | Bin 0 -> 4876 bytes .../__pycache__/proto_builder.cpython-38.pyc | Bin 0 -> 2931 bytes .../__pycache__/reflection.cpython-38.pyc | Bin 0 -> 1982 bytes .../__pycache__/service.cpython-38.pyc | Bin 0 -> 8882 bytes .../service_reflection.cpython-38.pyc | Bin 0 -> 10149 bytes .../source_context_pb2.cpython-38.pyc | Bin 0 -> 1355 bytes .../__pycache__/struct_pb2.cpython-38.pyc | Bin 0 -> 2303 bytes .../symbol_database.cpython-38.pyc | Bin 0 -> 5843 bytes .../__pycache__/text_encoding.cpython-38.pyc | Bin 0 -> 2740 bytes .../__pycache__/text_format.cpython-38.pyc | Bin 0 -> 44723 bytes .../__pycache__/timestamp_pb2.cpython-38.pyc | Bin 0 -> 1331 bytes .../__pycache__/type_pb2.cpython-38.pyc | Bin 0 -> 3568 bytes .../__pycache__/wrappers_pb2.cpython-38.pyc | Bin 0 -> 2449 bytes .../site-packages/google/protobuf/any_pb2.py | 35 + .../site-packages/google/protobuf/api_pb2.py | 57 + .../google/protobuf/compiler/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 214 bytes .../__pycache__/plugin_pb2.cpython-38.pyc | Bin 0 -> 2428 bytes .../google/protobuf/compiler/plugin_pb2.py | 70 + .../google/protobuf/descriptor.py | 1182 +++ .../google/protobuf/descriptor_database.py | 177 + .../google/protobuf/descriptor_pb2.py | 2147 +++++ .../google/protobuf/descriptor_pool.py | 1289 +++ .../google/protobuf/duration_pb2.py | 35 + .../google/protobuf/empty_pb2.py | 35 + .../google/protobuf/field_mask_pb2.py | 35 + .../google/protobuf/internal/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 214 bytes .../api_implementation.cpython-38.pyc | Bin 0 -> 1598 bytes .../__pycache__/containers.cpython-38.pyc | Bin 0 -> 20582 bytes .../__pycache__/decoder.cpython-38.pyc | Bin 0 -> 24252 bytes .../__pycache__/encoder.cpython-38.pyc | Bin 0 -> 23248 bytes .../enum_type_wrapper.cpython-38.pyc | Bin 0 -> 3449 bytes .../__pycache__/extension_dict.cpython-38.pyc | Bin 0 -> 5662 bytes .../message_listener.cpython-38.pyc | Bin 0 -> 2378 bytes .../__pycache__/python_message.cpython-38.pyc | Bin 0 -> 40943 bytes .../__pycache__/type_checkers.cpython-38.pyc | Bin 0 -> 10649 bytes .../well_known_types.cpython-38.pyc | Bin 0 -> 26557 bytes .../__pycache__/wire_format.cpython-38.pyc | Bin 0 -> 6457 bytes .../_api_implementation.cpython-38-darwin.so | Bin 0 -> 9352 bytes .../protobuf/internal/api_implementation.py | 112 + .../google/protobuf/internal/containers.py | 643 ++ .../google/protobuf/internal/decoder.py | 1029 +++ .../google/protobuf/internal/encoder.py | 829 ++ .../protobuf/internal/enum_type_wrapper.py | 115 + .../protobuf/internal/extension_dict.py | 213 + .../protobuf/internal/message_listener.py | 78 + .../protobuf/internal/python_message.py | 1539 ++++ .../google/protobuf/internal/type_checkers.py | 410 + .../protobuf/internal/well_known_types.py | 859 ++ .../google/protobuf/internal/wire_format.py | 268 + .../google/protobuf/json_format.py | 856 ++ .../site-packages/google/protobuf/message.py | 421 + .../google/protobuf/message_factory.py | 185 + .../google/protobuf/proto_builder.py | 134 + .../google/protobuf/pyext/__init__.py | 0 .../pyext/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 211 bytes .../__pycache__/cpp_message.cpython-38.pyc | Bin 0 -> 1494 bytes .../pyext/_message.cpython-38-darwin.so | Bin 0 -> 3089504 bytes .../google/protobuf/pyext/cpp_message.py | 65 + .../google/protobuf/reflection.py | 95 + .../site-packages/google/protobuf/service.py | 228 + .../google/protobuf/service_reflection.py | 287 + .../google/protobuf/source_context_pb2.py | 35 + .../google/protobuf/struct_pb2.py | 74 + .../google/protobuf/symbol_database.py | 194 + .../google/protobuf/text_encoding.py | 110 + .../google/protobuf/text_format.py | 1795 ++++ .../google/protobuf/timestamp_pb2.py | 35 + .../site-packages/google/protobuf/type_pb2.py | 90 + .../google/protobuf/util/__init__.py | 0 .../util/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 210 bytes .../json_format_pb2.cpython-38.pyc | Bin 0 -> 5382 bytes .../json_format_proto3_pb2.cpython-38.pyc | Bin 0 -> 10740 bytes .../google/protobuf/util/json_format_pb2.py | 236 + .../protobuf/util/json_format_proto3_pb2.py | 414 + .../google/protobuf/wrappers_pb2.py | 115 + .../greenlet-1.1.2.dist-info/AUTHORS | 51 + .../greenlet-1.1.2.dist-info/INSTALLER | 1 + .../greenlet-1.1.2.dist-info/LICENSE | 30 + .../greenlet-1.1.2.dist-info/LICENSE.PSF | 47 + .../greenlet-1.1.2.dist-info/METADATA | 104 + .../greenlet-1.1.2.dist-info/RECORD | 71 + .../greenlet-1.1.2.dist-info/WHEEL | 5 + .../greenlet-1.1.2.dist-info/top_level.txt | 1 + .../site-packages/greenlet/__init__.py | 63 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 889 bytes .../greenlet/_greenlet.cpython-38-darwin.so | Bin 0 -> 43800 bytes .../site-packages/greenlet/greenlet.c | 2135 +++++ .../site-packages/greenlet/greenlet.h | 146 + .../platform/setup_switch_x64_masm.cmd | 2 + .../greenlet/platform/switch_aarch64_gcc.h | 69 + .../greenlet/platform/switch_alpha_unix.h | 30 + .../greenlet/platform/switch_amd64_unix.h | 84 + .../greenlet/platform/switch_arm32_gcc.h | 79 + .../greenlet/platform/switch_arm32_ios.h | 67 + .../greenlet/platform/switch_csky_gcc.h | 48 + .../greenlet/platform/switch_m68k_gcc.h | 38 + .../greenlet/platform/switch_mips_unix.h | 64 + .../greenlet/platform/switch_ppc64_aix.h | 103 + .../greenlet/platform/switch_ppc64_linux.h | 105 + .../greenlet/platform/switch_ppc_aix.h | 87 + .../greenlet/platform/switch_ppc_linux.h | 84 + .../greenlet/platform/switch_ppc_macosx.h | 82 + .../greenlet/platform/switch_ppc_unix.h | 82 + .../greenlet/platform/switch_riscv_unix.h | 32 + .../greenlet/platform/switch_s390_unix.h | 87 + .../greenlet/platform/switch_sparc_sun_gcc.h | 92 + .../greenlet/platform/switch_x32_unix.h | 63 + .../greenlet/platform/switch_x64_masm.asm | 111 + .../greenlet/platform/switch_x64_masm.obj | Bin 0 -> 1078 bytes .../greenlet/platform/switch_x64_msvc.h | 60 + .../greenlet/platform/switch_x86_msvc.h | 88 + .../greenlet/platform/switch_x86_unix.h | 105 + .../greenlet/slp_platformselect.h | 58 + .../site-packages/greenlet/tests/__init__.py | 0 .../tests/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 204 bytes .../test_contextvars.cpython-38.pyc | Bin 0 -> 7247 bytes .../tests/__pycache__/test_cpp.cpython-38.pyc | Bin 0 -> 870 bytes .../test_extension_interface.cpython-38.pyc | Bin 0 -> 3583 bytes .../tests/__pycache__/test_gc.cpython-38.pyc | Bin 0 -> 2918 bytes .../__pycache__/test_generator.cpython-38.pyc | Bin 0 -> 2226 bytes .../test_generator_nested.cpython-38.pyc | Bin 0 -> 5152 bytes .../__pycache__/test_greenlet.cpython-38.pyc | Bin 0 -> 27079 bytes .../__pycache__/test_leaks.cpython-38.pyc | Bin 0 -> 5163 bytes .../test_stack_saved.cpython-38.pyc | Bin 0 -> 898 bytes .../__pycache__/test_throw.cpython-38.pyc | Bin 0 -> 2980 bytes .../__pycache__/test_tracing.cpython-38.pyc | Bin 0 -> 9017 bytes .../__pycache__/test_version.cpython-38.pyc | Bin 0 -> 1532 bytes .../__pycache__/test_weakref.cpython-38.pyc | Bin 0 -> 1861 bytes .../greenlet/tests/_test_extension.c | 216 + .../_test_extension.cpython-38-darwin.so | Bin 0 -> 35296 bytes .../greenlet/tests/_test_extension_cpp.cpp | 121 + .../_test_extension_cpp.cpython-38-darwin.so | Bin 0 -> 35208 bytes .../greenlet/tests/test_contextvars.py | 266 + .../site-packages/greenlet/tests/test_cpp.py | 18 + .../tests/test_extension_interface.py | 77 + .../site-packages/greenlet/tests/test_gc.py | 77 + .../greenlet/tests/test_generator.py | 59 + .../greenlet/tests/test_generator_nested.py | 165 + .../greenlet/tests/test_greenlet.py | 728 ++ .../greenlet/tests/test_leaks.py | 178 + .../greenlet/tests/test_stack_saved.py | 19 + .../greenlet/tests/test_throw.py | 100 + .../greenlet/tests/test_tracing.py | 267 + .../greenlet/tests/test_version.py | 39 + .../greenlet/tests/test_weakref.py | 34 + .../itsdangerous-2.0.1.dist-info/INSTALLER | 1 + .../itsdangerous-2.0.1.dist-info/LICENSE.rst | 28 + .../itsdangerous-2.0.1.dist-info/METADATA | 96 + .../itsdangerous-2.0.1.dist-info/RECORD | 25 + .../itsdangerous-2.0.1.dist-info/WHEEL | 5 + .../top_level.txt | 1 + .../site-packages/itsdangerous/__init__.py | 22 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1034 bytes .../__pycache__/_json.cpython-38.pyc | Bin 0 -> 1564 bytes .../__pycache__/encoding.cpython-38.pyc | Bin 0 -> 1888 bytes .../__pycache__/exc.cpython-38.pyc | Bin 0 -> 3461 bytes .../__pycache__/jws.cpython-38.pyc | Bin 0 -> 7592 bytes .../__pycache__/serializer.cpython-38.pyc | Bin 0 -> 9747 bytes .../__pycache__/signer.cpython-38.pyc | Bin 0 -> 8470 bytes .../__pycache__/timed.cpython-38.pyc | Bin 0 -> 6387 bytes .../__pycache__/url_safe.cpython-38.pyc | Bin 0 -> 2719 bytes .../site-packages/itsdangerous/_json.py | 34 + .../site-packages/itsdangerous/encoding.py | 54 + .../site-packages/itsdangerous/exc.py | 107 + .../site-packages/itsdangerous/jws.py | 259 + .../site-packages/itsdangerous/py.typed | 0 .../site-packages/itsdangerous/serializer.py | 295 + .../site-packages/itsdangerous/signer.py | 257 + .../site-packages/itsdangerous/timed.py | 227 + .../site-packages/itsdangerous/url_safe.py | 80 + .../site-packages/jinja2/__init__.py | 45 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1927 bytes .../__pycache__/_identifier.cpython-38.pyc | Bin 0 -> 1914 bytes .../__pycache__/async_utils.cpython-38.pyc | Bin 0 -> 2454 bytes .../jinja2/__pycache__/bccache.cpython-38.pyc | Bin 0 -> 13276 bytes .../__pycache__/compiler.cpython-38.pyc | Bin 0 -> 54287 bytes .../__pycache__/constants.cpython-38.pyc | Bin 0 -> 1558 bytes .../jinja2/__pycache__/debug.cpython-38.pyc | Bin 0 -> 5435 bytes .../__pycache__/defaults.cpython-38.pyc | Bin 0 -> 1360 bytes .../__pycache__/environment.cpython-38.pyc | Bin 0 -> 52910 bytes .../__pycache__/exceptions.cpython-38.pyc | Bin 0 -> 5601 bytes .../jinja2/__pycache__/ext.cpython-38.pyc | Bin 0 -> 26391 bytes .../jinja2/__pycache__/filters.cpython-38.pyc | Bin 0 -> 50193 bytes .../__pycache__/idtracking.cpython-38.pyc | Bin 0 -> 11107 bytes .../jinja2/__pycache__/lexer.cpython-38.pyc | Bin 0 -> 20388 bytes .../jinja2/__pycache__/loaders.cpython-38.pyc | Bin 0 -> 20402 bytes .../jinja2/__pycache__/meta.cpython-38.pyc | Bin 0 -> 3818 bytes .../__pycache__/nativetypes.cpython-38.pyc | Bin 0 -> 4886 bytes .../jinja2/__pycache__/nodes.cpython-38.pyc | Bin 0 -> 40921 bytes .../__pycache__/optimizer.cpython-38.pyc | Bin 0 -> 1947 bytes .../jinja2/__pycache__/parser.cpython-38.pyc | Bin 0 -> 27553 bytes .../jinja2/__pycache__/runtime.cpython-38.pyc | Bin 0 -> 33198 bytes .../jinja2/__pycache__/sandbox.cpython-38.pyc | Bin 0 -> 11959 bytes .../jinja2/__pycache__/tests.cpython-38.pyc | Bin 0 -> 6594 bytes .../jinja2/__pycache__/utils.cpython-38.pyc | Bin 0 -> 27434 bytes .../jinja2/__pycache__/visitor.cpython-38.pyc | Bin 0 -> 3876 bytes .../site-packages/jinja2/_identifier.py | 6 + .../site-packages/jinja2/async_utils.py | 75 + .../python3.8/site-packages/jinja2/bccache.py | 364 + .../site-packages/jinja2/compiler.py | 1957 ++++ .../site-packages/jinja2/constants.py | 20 + .../python3.8/site-packages/jinja2/debug.py | 259 + .../site-packages/jinja2/defaults.py | 48 + .../site-packages/jinja2/environment.py | 1661 ++++ .../site-packages/jinja2/exceptions.py | 166 + .../lib/python3.8/site-packages/jinja2/ext.py | 879 ++ .../python3.8/site-packages/jinja2/filters.py | 1824 ++++ .../site-packages/jinja2/idtracking.py | 318 + .../python3.8/site-packages/jinja2/lexer.py | 869 ++ .../python3.8/site-packages/jinja2/loaders.py | 652 ++ .../python3.8/site-packages/jinja2/meta.py | 111 + .../site-packages/jinja2/nativetypes.py | 124 + .../python3.8/site-packages/jinja2/nodes.py | 1204 +++ .../site-packages/jinja2/optimizer.py | 47 + .../python3.8/site-packages/jinja2/parser.py | 1040 +++ .../python3.8/site-packages/jinja2/py.typed | 0 .../python3.8/site-packages/jinja2/runtime.py | 1104 +++ .../python3.8/site-packages/jinja2/sandbox.py | 428 + .../python3.8/site-packages/jinja2/tests.py | 255 + .../python3.8/site-packages/jinja2/utils.py | 854 ++ .../python3.8/site-packages/jinja2/visitor.py | 92 + .../site-packages/markupsafe/__init__.py | 288 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 10739 bytes .../__pycache__/_native.cpython-38.pyc | Bin 0 -> 2364 bytes .../site-packages/markupsafe/_native.py | 75 + .../site-packages/markupsafe/_speedups.c | 339 + .../markupsafe/_speedups.cpython-38-darwin.so | Bin 0 -> 35480 bytes .../site-packages/markupsafe/_speedups.pyi | 9 + .../site-packages/markupsafe/py.typed | 0 .../packaging-21.2.dist-info/INSTALLER | 1 + .../packaging-21.2.dist-info/LICENSE | 3 + .../packaging-21.2.dist-info/LICENSE.APACHE | 177 + .../packaging-21.2.dist-info/LICENSE.BSD | 23 + .../packaging-21.2.dist-info/METADATA | 446 + .../packaging-21.2.dist-info/RECORD | 31 + .../packaging-21.2.dist-info/WHEEL | 5 + .../packaging-21.2.dist-info/top_level.txt | 1 + .../site-packages/packaging/__about__.py | 26 + .../site-packages/packaging/__init__.py | 25 + .../__pycache__/__about__.cpython-38.pyc | Bin 0 -> 623 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 479 bytes .../__pycache__/_manylinux.cpython-38.pyc | Bin 0 -> 7297 bytes .../__pycache__/_musllinux.cpython-38.pyc | Bin 0 -> 4630 bytes .../__pycache__/_structures.cpython-38.pyc | Bin 0 -> 3089 bytes .../__pycache__/markers.cpython-38.pyc | Bin 0 -> 9474 bytes .../__pycache__/requirements.cpython-38.pyc | Bin 0 -> 3971 bytes .../__pycache__/specifiers.cpython-38.pyc | Bin 0 -> 22263 bytes .../packaging/__pycache__/tags.cpython-38.pyc | Bin 0 -> 12294 bytes .../__pycache__/utils.cpython-38.pyc | Bin 0 -> 3624 bytes .../__pycache__/version.cpython-38.pyc | Bin 0 -> 13179 bytes .../site-packages/packaging/_manylinux.py | 301 + .../site-packages/packaging/_musllinux.py | 136 + .../site-packages/packaging/_structures.py | 67 + .../site-packages/packaging/markers.py | 304 + .../site-packages/packaging/py.typed | 0 .../site-packages/packaging/requirements.py | 146 + .../site-packages/packaging/specifiers.py | 828 ++ .../python3.8/site-packages/packaging/tags.py | 484 + .../site-packages/packaging/utils.py | 136 + .../site-packages/packaging/version.py | 504 + .../pip-19.2.3.dist-info/INSTALLER | 1 + .../pip-19.2.3.dist-info/LICENSE.txt | 20 + .../pip-19.2.3.dist-info/METADATA | 81 + .../site-packages/pip-19.2.3.dist-info/RECORD | 668 ++ .../site-packages/pip-19.2.3.dist-info/WHEEL | 6 + .../pip-19.2.3.dist-info/entry_points.txt | 5 + .../pip-19.2.3.dist-info/top_level.txt | 1 + .../python3.8/site-packages/pip/__init__.py | 1 + .../python3.8/site-packages/pip/__main__.py | 19 + .../pip/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 210 bytes .../pip/__pycache__/__main__.cpython-38.pyc | Bin 0 -> 462 bytes .../site-packages/pip/_internal/__init__.py | 77 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1755 bytes .../__pycache__/build_env.cpython-38.pyc | Bin 0 -> 7531 bytes .../__pycache__/cache.cpython-38.pyc | Bin 0 -> 7143 bytes .../__pycache__/configuration.cpython-38.pyc | Bin 0 -> 10694 bytes .../__pycache__/download.cpython-38.pyc | Bin 0 -> 25981 bytes .../__pycache__/exceptions.cpython-38.pyc | Bin 0 -> 12542 bytes .../__pycache__/index.cpython-38.pyc | Bin 0 -> 37782 bytes .../__pycache__/legacy_resolve.cpython-38.pyc | Bin 0 -> 10601 bytes .../__pycache__/locations.cpython-38.pyc | Bin 0 -> 2990 bytes .../__pycache__/pep425tags.cpython-38.pyc | Bin 0 -> 8389 bytes .../__pycache__/pyproject.cpython-38.pyc | Bin 0 -> 3216 bytes .../__pycache__/wheel.cpython-38.pyc | Bin 0 -> 27542 bytes .../site-packages/pip/_internal/build_env.py | 218 + .../site-packages/pip/_internal/cache.py | 224 + .../pip/_internal/cli/__init__.py | 4 + .../cli/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 285 bytes .../__pycache__/autocompletion.cpython-38.pyc | Bin 0 -> 5143 bytes .../__pycache__/base_command.cpython-38.pyc | Bin 0 -> 8318 bytes .../cli/__pycache__/cmdoptions.cpython-38.pyc | Bin 0 -> 20060 bytes .../__pycache__/main_parser.cpython-38.pyc | Bin 0 -> 2223 bytes .../cli/__pycache__/parser.cpython-38.pyc | Bin 0 -> 9026 bytes .../__pycache__/status_codes.cpython-38.pyc | Bin 0 -> 414 bytes .../pip/_internal/cli/autocompletion.py | 152 + .../pip/_internal/cli/base_command.py | 346 + .../pip/_internal/cli/cmdoptions.py | 931 ++ .../pip/_internal/cli/main_parser.py | 98 + .../site-packages/pip/_internal/cli/parser.py | 261 + .../pip/_internal/cli/status_codes.py | 8 + .../pip/_internal/commands/__init__.py | 81 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2604 bytes .../commands/__pycache__/check.cpython-38.pyc | Bin 0 -> 1346 bytes .../__pycache__/completion.cpython-38.pyc | Bin 0 -> 3125 bytes .../__pycache__/configuration.cpython-38.pyc | Bin 0 -> 7189 bytes .../commands/__pycache__/debug.cpython-38.pyc | Bin 0 -> 3397 bytes .../__pycache__/download.cpython-38.pyc | Bin 0 -> 4751 bytes .../__pycache__/freeze.cpython-38.pyc | Bin 0 -> 3044 bytes .../commands/__pycache__/hash.cpython-38.pyc | Bin 0 -> 2094 bytes .../commands/__pycache__/help.cpython-38.pyc | Bin 0 -> 1265 bytes .../__pycache__/install.cpython-38.pyc | Bin 0 -> 13092 bytes .../commands/__pycache__/list.cpython-38.pyc | Bin 0 -> 9033 bytes .../__pycache__/search.cpython-38.pyc | Bin 0 -> 4480 bytes .../commands/__pycache__/show.cpython-38.pyc | Bin 0 -> 5877 bytes .../__pycache__/uninstall.cpython-38.pyc | Bin 0 -> 2727 bytes .../commands/__pycache__/wheel.cpython-38.pyc | Bin 0 -> 4933 bytes .../pip/_internal/commands/check.py | 41 + .../pip/_internal/commands/completion.py | 94 + .../pip/_internal/commands/configuration.py | 258 + .../pip/_internal/commands/debug.py | 114 + .../pip/_internal/commands/download.py | 168 + .../pip/_internal/commands/freeze.py | 101 + .../pip/_internal/commands/hash.py | 57 + .../pip/_internal/commands/help.py | 37 + .../pip/_internal/commands/install.py | 580 ++ .../pip/_internal/commands/list.py | 311 + .../pip/_internal/commands/search.py | 139 + .../pip/_internal/commands/show.py | 168 + .../pip/_internal/commands/uninstall.py | 78 + .../pip/_internal/commands/wheel.py | 181 + .../pip/_internal/configuration.py | 417 + .../pip/_internal/distributions/__init__.py | 23 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 874 bytes .../__pycache__/base.cpython-38.pyc | Bin 0 -> 1669 bytes .../__pycache__/installed.cpython-38.pyc | Bin 0 -> 1009 bytes .../__pycache__/source.cpython-38.pyc | Bin 0 -> 3106 bytes .../__pycache__/wheel.cpython-38.pyc | Bin 0 -> 1073 bytes .../pip/_internal/distributions/base.py | 33 + .../pip/_internal/distributions/installed.py | 15 + .../pip/_internal/distributions/source.py | 80 + .../pip/_internal/distributions/wheel.py | 17 + .../site-packages/pip/_internal/download.py | 1177 +++ .../site-packages/pip/_internal/exceptions.py | 305 + .../site-packages/pip/_internal/index.py | 1508 +++ .../pip/_internal/legacy_resolve.py | 457 + .../site-packages/pip/_internal/locations.py | 142 + .../pip/_internal/models/__init__.py | 2 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 273 bytes .../__pycache__/candidate.cpython-38.pyc | Bin 0 -> 1507 bytes .../__pycache__/format_control.cpython-38.pyc | Bin 0 -> 2301 bytes .../models/__pycache__/index.cpython-38.pyc | Bin 0 -> 1195 bytes .../models/__pycache__/link.cpython-38.pyc | Bin 0 -> 6334 bytes .../__pycache__/search_scope.cpython-38.pyc | Bin 0 -> 3304 bytes .../selection_prefs.cpython-38.pyc | Bin 0 -> 1645 bytes .../__pycache__/target_python.cpython-38.pyc | Bin 0 -> 3249 bytes .../pip/_internal/models/candidate.py | 36 + .../pip/_internal/models/format_control.py | 73 + .../pip/_internal/models/index.py | 31 + .../pip/_internal/models/link.py | 213 + .../pip/_internal/models/search_scope.py | 113 + .../pip/_internal/models/selection_prefs.py | 47 + .../pip/_internal/models/target_python.py | 106 + .../pip/_internal/operations/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 209 bytes .../__pycache__/check.cpython-38.pyc | Bin 0 -> 3715 bytes .../__pycache__/freeze.cpython-38.pyc | Bin 0 -> 5779 bytes .../__pycache__/prepare.cpython-38.pyc | Bin 0 -> 5796 bytes .../pip/_internal/operations/check.py | 159 + .../pip/_internal/operations/freeze.py | 253 + .../pip/_internal/operations/prepare.py | 287 + .../site-packages/pip/_internal/pep425tags.py | 384 + .../site-packages/pip/_internal/pyproject.py | 171 + .../pip/_internal/req/__init__.py | 78 + .../req/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1749 bytes .../__pycache__/constructors.cpython-38.pyc | Bin 0 -> 8021 bytes .../req/__pycache__/req_file.cpython-38.pyc | Bin 0 -> 9426 bytes .../__pycache__/req_install.cpython-38.pyc | Bin 0 -> 25779 bytes .../req/__pycache__/req_set.cpython-38.pyc | Bin 0 -> 5742 bytes .../__pycache__/req_tracker.cpython-38.pyc | Bin 0 -> 3274 bytes .../__pycache__/req_uninstall.cpython-38.pyc | Bin 0 -> 17401 bytes .../pip/_internal/req/constructors.py | 349 + .../pip/_internal/req/req_file.py | 399 + .../pip/_internal/req/req_install.py | 1035 +++ .../pip/_internal/req/req_set.py | 193 + .../pip/_internal/req/req_tracker.py | 96 + .../pip/_internal/req/req_uninstall.py | 633 ++ .../pip/_internal/utils/__init__.py | 0 .../utils/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 204 bytes .../utils/__pycache__/appdirs.cpython-38.pyc | Bin 0 -> 8083 bytes .../utils/__pycache__/compat.cpython-38.pyc | Bin 0 -> 6979 bytes .../__pycache__/deprecation.cpython-38.pyc | Bin 0 -> 2875 bytes .../utils/__pycache__/encoding.cpython-38.pyc | Bin 0 -> 1296 bytes .../__pycache__/filesystem.cpython-38.pyc | Bin 0 -> 675 bytes .../utils/__pycache__/glibc.cpython-38.pyc | Bin 0 -> 2306 bytes .../utils/__pycache__/hashes.cpython-38.pyc | Bin 0 -> 4193 bytes .../utils/__pycache__/logging.cpython-38.pyc | Bin 0 -> 9169 bytes .../__pycache__/marker_files.cpython-38.pyc | Bin 0 -> 815 bytes .../utils/__pycache__/misc.cpython-38.pyc | Bin 0 -> 29900 bytes .../utils/__pycache__/models.cpython-38.pyc | Bin 0 -> 1973 bytes .../utils/__pycache__/outdated.cpython-38.pyc | Bin 0 -> 4260 bytes .../__pycache__/packaging.cpython-38.pyc | Bin 0 -> 2657 bytes .../setuptools_build.cpython-38.pyc | Bin 0 -> 1066 bytes .../utils/__pycache__/temp_dir.cpython-38.pyc | Bin 0 -> 4924 bytes .../utils/__pycache__/typing.cpython-38.pyc | Bin 0 -> 1334 bytes .../utils/__pycache__/ui.cpython-38.pyc | Bin 0 -> 11859 bytes .../__pycache__/virtualenv.cpython-38.pyc | Bin 0 -> 927 bytes .../pip/_internal/utils/appdirs.py | 268 + .../pip/_internal/utils/compat.py | 293 + .../pip/_internal/utils/deprecation.py | 100 + .../pip/_internal/utils/encoding.py | 39 + .../pip/_internal/utils/filesystem.py | 30 + .../pip/_internal/utils/glibc.py | 120 + .../pip/_internal/utils/hashes.py | 128 + .../pip/_internal/utils/logging.py | 394 + .../pip/_internal/utils/marker_files.py | 20 + .../site-packages/pip/_internal/utils/misc.py | 1201 +++ .../pip/_internal/utils/models.py | 40 + .../pip/_internal/utils/outdated.py | 178 + .../pip/_internal/utils/packaging.py | 94 + .../pip/_internal/utils/setuptools_build.py | 36 + .../pip/_internal/utils/temp_dir.py | 155 + .../pip/_internal/utils/typing.py | 29 + .../site-packages/pip/_internal/utils/ui.py | 424 + .../pip/_internal/utils/virtualenv.py | 34 + .../pip/_internal/vcs/__init__.py | 12 + .../vcs/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 480 bytes .../vcs/__pycache__/bazaar.cpython-38.pyc | Bin 0 -> 3456 bytes .../vcs/__pycache__/git.cpython-38.pyc | Bin 0 -> 9012 bytes .../vcs/__pycache__/mercurial.cpython-38.pyc | Bin 0 -> 3743 bytes .../vcs/__pycache__/subversion.cpython-38.pyc | Bin 0 -> 8396 bytes .../__pycache__/versioncontrol.cpython-38.pyc | Bin 0 -> 17126 bytes .../site-packages/pip/_internal/vcs/bazaar.py | 101 + .../site-packages/pip/_internal/vcs/git.py | 358 + .../pip/_internal/vcs/mercurial.py | 103 + .../pip/_internal/vcs/subversion.py | 314 + .../pip/_internal/vcs/versioncontrol.py | 600 ++ .../site-packages/pip/_internal/wheel.py | 1125 +++ .../site-packages/pip/_vendor/__init__.py | 109 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2875 bytes .../__pycache__/appdirs.cpython-38.pyc | Bin 0 -> 20486 bytes .../_vendor/__pycache__/distro.cpython-38.pyc | Bin 0 -> 36590 bytes .../__pycache__/ipaddress.cpython-38.pyc | Bin 0 -> 64831 bytes .../__pycache__/pyparsing.cpython-38.pyc | Bin 0 -> 220254 bytes .../__pycache__/retrying.cpython-38.pyc | Bin 0 -> 8055 bytes .../_vendor/__pycache__/six.cpython-38.pyc | Bin 0 -> 26445 bytes .../site-packages/pip/_vendor/appdirs.py | 604 ++ .../pip/_vendor/cachecontrol/__init__.py | 11 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 562 bytes .../__pycache__/_cmd.cpython-38.pyc | Bin 0 -> 1589 bytes .../__pycache__/adapter.cpython-38.pyc | Bin 0 -> 3088 bytes .../__pycache__/cache.cpython-38.pyc | Bin 0 -> 1795 bytes .../__pycache__/compat.cpython-38.pyc | Bin 0 -> 769 bytes .../__pycache__/controller.cpython-38.pyc | Bin 0 -> 7709 bytes .../__pycache__/filewrapper.cpython-38.pyc | Bin 0 -> 2188 bytes .../__pycache__/heuristics.cpython-38.pyc | Bin 0 -> 4747 bytes .../__pycache__/serialize.cpython-38.pyc | Bin 0 -> 4260 bytes .../__pycache__/wrapper.cpython-38.pyc | Bin 0 -> 678 bytes .../pip/_vendor/cachecontrol/_cmd.py | 57 + .../pip/_vendor/cachecontrol/adapter.py | 133 + .../pip/_vendor/cachecontrol/cache.py | 39 + .../_vendor/cachecontrol/caches/__init__.py | 2 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 306 bytes .../__pycache__/file_cache.cpython-38.pyc | Bin 0 -> 3304 bytes .../__pycache__/redis_cache.cpython-38.pyc | Bin 0 -> 1578 bytes .../_vendor/cachecontrol/caches/file_cache.py | 146 + .../cachecontrol/caches/redis_cache.py | 33 + .../pip/_vendor/cachecontrol/compat.py | 29 + .../pip/_vendor/cachecontrol/controller.py | 367 + .../pip/_vendor/cachecontrol/filewrapper.py | 80 + .../pip/_vendor/cachecontrol/heuristics.py | 135 + .../pip/_vendor/cachecontrol/serialize.py | 186 + .../pip/_vendor/cachecontrol/wrapper.py | 29 + .../pip/_vendor/certifi/__init__.py | 3 + .../pip/_vendor/certifi/__main__.py | 2 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 269 bytes .../__pycache__/__main__.cpython-38.pyc | Bin 0 -> 272 bytes .../certifi/__pycache__/core.cpython-38.pyc | Bin 0 -> 485 bytes .../pip/_vendor/certifi/cacert.pem | 4618 ++++++++++ .../site-packages/pip/_vendor/certifi/core.py | 15 + .../pip/_vendor/chardet/__init__.py | 39 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 862 bytes .../__pycache__/big5freq.cpython-38.pyc | Bin 0 -> 27191 bytes .../__pycache__/big5prober.cpython-38.pyc | Bin 0 -> 1146 bytes .../chardistribution.cpython-38.pyc | Bin 0 -> 6232 bytes .../charsetgroupprober.cpython-38.pyc | Bin 0 -> 2263 bytes .../__pycache__/charsetprober.cpython-38.pyc | Bin 0 -> 3495 bytes .../codingstatemachine.cpython-38.pyc | Bin 0 -> 2922 bytes .../chardet/__pycache__/compat.cpython-38.pyc | Bin 0 -> 367 bytes .../__pycache__/cp949prober.cpython-38.pyc | Bin 0 -> 1153 bytes .../chardet/__pycache__/enums.cpython-38.pyc | Bin 0 -> 2660 bytes .../__pycache__/escprober.cpython-38.pyc | Bin 0 -> 2645 bytes .../chardet/__pycache__/escsm.cpython-38.pyc | Bin 0 -> 7486 bytes .../__pycache__/eucjpprober.cpython-38.pyc | Bin 0 -> 2463 bytes .../__pycache__/euckrfreq.cpython-38.pyc | Bin 0 -> 12075 bytes .../__pycache__/euckrprober.cpython-38.pyc | Bin 0 -> 1154 bytes .../__pycache__/euctwfreq.cpython-38.pyc | Bin 0 -> 27195 bytes .../__pycache__/euctwprober.cpython-38.pyc | Bin 0 -> 1154 bytes .../__pycache__/gb2312freq.cpython-38.pyc | Bin 0 -> 19119 bytes .../__pycache__/gb2312prober.cpython-38.pyc | Bin 0 -> 1162 bytes .../__pycache__/hebrewprober.cpython-38.pyc | Bin 0 -> 3035 bytes .../__pycache__/jisfreq.cpython-38.pyc | Bin 0 -> 22147 bytes .../chardet/__pycache__/jpcntx.cpython-38.pyc | Bin 0 -> 37620 bytes .../langbulgarianmodel.cpython-38.pyc | Bin 0 -> 23644 bytes .../langcyrillicmodel.cpython-38.pyc | Bin 0 -> 29108 bytes .../__pycache__/langgreekmodel.cpython-38.pyc | Bin 0 -> 23602 bytes .../langhebrewmodel.cpython-38.pyc | Bin 0 -> 22229 bytes .../langhungarianmodel.cpython-38.pyc | Bin 0 -> 23633 bytes .../__pycache__/langthaimodel.cpython-38.pyc | Bin 0 -> 22208 bytes .../langturkishmodel.cpython-38.pyc | Bin 0 -> 22231 bytes .../__pycache__/latin1prober.cpython-38.pyc | Bin 0 -> 3415 bytes .../mbcharsetprober.cpython-38.pyc | Bin 0 -> 2278 bytes .../mbcsgroupprober.cpython-38.pyc | Bin 0 -> 1143 bytes .../chardet/__pycache__/mbcssm.cpython-38.pyc | Bin 0 -> 16766 bytes .../sbcharsetprober.cpython-38.pyc | Bin 0 -> 3031 bytes .../sbcsgroupprober.cpython-38.pyc | Bin 0 -> 1641 bytes .../__pycache__/sjisprober.cpython-38.pyc | Bin 0 -> 2499 bytes .../universaldetector.cpython-38.pyc | Bin 0 -> 5843 bytes .../__pycache__/utf8prober.cpython-38.pyc | Bin 0 -> 2004 bytes .../__pycache__/version.cpython-38.pyc | Bin 0 -> 451 bytes .../pip/_vendor/chardet/big5freq.py | 386 + .../pip/_vendor/chardet/big5prober.py | 47 + .../pip/_vendor/chardet/chardistribution.py | 233 + .../pip/_vendor/chardet/charsetgroupprober.py | 106 + .../pip/_vendor/chardet/charsetprober.py | 145 + .../pip/_vendor/chardet/cli/__init__.py | 1 + .../cli/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 208 bytes .../cli/__pycache__/chardetect.cpython-38.pyc | Bin 0 -> 2709 bytes .../pip/_vendor/chardet/cli/chardetect.py | 85 + .../pip/_vendor/chardet/codingstatemachine.py | 88 + .../pip/_vendor/chardet/compat.py | 34 + .../pip/_vendor/chardet/cp949prober.py | 49 + .../pip/_vendor/chardet/enums.py | 76 + .../pip/_vendor/chardet/escprober.py | 101 + .../pip/_vendor/chardet/escsm.py | 246 + .../pip/_vendor/chardet/eucjpprober.py | 92 + .../pip/_vendor/chardet/euckrfreq.py | 195 + .../pip/_vendor/chardet/euckrprober.py | 47 + .../pip/_vendor/chardet/euctwfreq.py | 387 + .../pip/_vendor/chardet/euctwprober.py | 46 + .../pip/_vendor/chardet/gb2312freq.py | 283 + .../pip/_vendor/chardet/gb2312prober.py | 46 + .../pip/_vendor/chardet/hebrewprober.py | 292 + .../pip/_vendor/chardet/jisfreq.py | 325 + .../pip/_vendor/chardet/jpcntx.py | 233 + .../pip/_vendor/chardet/langbulgarianmodel.py | 228 + .../pip/_vendor/chardet/langcyrillicmodel.py | 333 + .../pip/_vendor/chardet/langgreekmodel.py | 225 + .../pip/_vendor/chardet/langhebrewmodel.py | 200 + .../pip/_vendor/chardet/langhungarianmodel.py | 225 + .../pip/_vendor/chardet/langthaimodel.py | 199 + .../pip/_vendor/chardet/langturkishmodel.py | 193 + .../pip/_vendor/chardet/latin1prober.py | 145 + .../pip/_vendor/chardet/mbcharsetprober.py | 91 + .../pip/_vendor/chardet/mbcsgroupprober.py | 54 + .../pip/_vendor/chardet/mbcssm.py | 572 ++ .../pip/_vendor/chardet/sbcharsetprober.py | 132 + .../pip/_vendor/chardet/sbcsgroupprober.py | 73 + .../pip/_vendor/chardet/sjisprober.py | 92 + .../pip/_vendor/chardet/universaldetector.py | 286 + .../pip/_vendor/chardet/utf8prober.py | 82 + .../pip/_vendor/chardet/version.py | 9 + .../pip/_vendor/colorama/__init__.py | 6 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 456 bytes .../colorama/__pycache__/ansi.cpython-38.pyc | Bin 0 -> 3241 bytes .../__pycache__/ansitowin32.cpython-38.pyc | Bin 0 -> 7750 bytes .../__pycache__/initialise.cpython-38.pyc | Bin 0 -> 1717 bytes .../colorama/__pycache__/win32.cpython-38.pyc | Bin 0 -> 3993 bytes .../__pycache__/winterm.cpython-38.pyc | Bin 0 -> 4677 bytes .../pip/_vendor/colorama/ansi.py | 102 + .../pip/_vendor/colorama/ansitowin32.py | 257 + .../pip/_vendor/colorama/initialise.py | 80 + .../pip/_vendor/colorama/win32.py | 152 + .../pip/_vendor/colorama/winterm.py | 169 + .../pip/_vendor/distlib/__init__.py | 23 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1077 bytes .../distlib/__pycache__/compat.cpython-38.pyc | Bin 0 -> 32220 bytes .../__pycache__/database.cpython-38.pyc | Bin 0 -> 42117 bytes .../distlib/__pycache__/index.cpython-38.pyc | Bin 0 -> 17424 bytes .../__pycache__/locators.cpython-38.pyc | Bin 0 -> 38322 bytes .../__pycache__/manifest.cpython-38.pyc | Bin 0 -> 10246 bytes .../__pycache__/markers.cpython-38.pyc | Bin 0 -> 4510 bytes .../__pycache__/metadata.cpython-38.pyc | Bin 0 -> 26657 bytes .../__pycache__/resources.cpython-38.pyc | Bin 0 -> 11021 bytes .../__pycache__/scripts.cpython-38.pyc | Bin 0 -> 10803 bytes .../distlib/__pycache__/util.cpython-38.pyc | Bin 0 -> 48154 bytes .../__pycache__/version.cpython-38.pyc | Bin 0 -> 20380 bytes .../distlib/__pycache__/wheel.cpython-38.pyc | Bin 0 -> 25567 bytes .../pip/_vendor/distlib/_backport/__init__.py | 6 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 496 bytes .../_backport/__pycache__/misc.cpython-38.pyc | Bin 0 -> 1107 bytes .../__pycache__/shutil.cpython-38.pyc | Bin 0 -> 21500 bytes .../__pycache__/sysconfig.cpython-38.pyc | Bin 0 -> 15899 bytes .../__pycache__/tarfile.cpython-38.pyc | Bin 0 -> 62756 bytes .../pip/_vendor/distlib/_backport/misc.py | 41 + .../pip/_vendor/distlib/_backport/shutil.py | 761 ++ .../_vendor/distlib/_backport/sysconfig.cfg | 84 + .../_vendor/distlib/_backport/sysconfig.py | 788 ++ .../pip/_vendor/distlib/_backport/tarfile.py | 2607 ++++++ .../pip/_vendor/distlib/compat.py | 1120 +++ .../pip/_vendor/distlib/database.py | 1339 +++ .../pip/_vendor/distlib/index.py | 516 ++ .../pip/_vendor/distlib/locators.py | 1295 +++ .../pip/_vendor/distlib/manifest.py | 393 + .../pip/_vendor/distlib/markers.py | 131 + .../pip/_vendor/distlib/metadata.py | 1096 +++ .../pip/_vendor/distlib/resources.py | 355 + .../pip/_vendor/distlib/scripts.py | 403 + .../site-packages/pip/_vendor/distlib/t32.exe | Bin 0 -> 92672 bytes .../site-packages/pip/_vendor/distlib/t64.exe | Bin 0 -> 102912 bytes .../site-packages/pip/_vendor/distlib/util.py | 1760 ++++ .../pip/_vendor/distlib/version.py | 736 ++ .../site-packages/pip/_vendor/distlib/w32.exe | Bin 0 -> 89088 bytes .../site-packages/pip/_vendor/distlib/w64.exe | Bin 0 -> 99840 bytes .../pip/_vendor/distlib/wheel.py | 1004 ++ .../site-packages/pip/_vendor/distro.py | 1216 +++ .../pip/_vendor/html5lib/__init__.py | 35 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1329 bytes .../__pycache__/_ihatexml.cpython-38.pyc | Bin 0 -> 13801 bytes .../__pycache__/_inputstream.cpython-38.pyc | Bin 0 -> 21953 bytes .../__pycache__/_tokenizer.cpython-38.pyc | Bin 0 -> 39671 bytes .../__pycache__/_utils.cpython-38.pyc | Bin 0 -> 3359 bytes .../__pycache__/constants.cpython-38.pyc | Bin 0 -> 66366 bytes .../__pycache__/html5parser.cpython-38.pyc | Bin 0 -> 95188 bytes .../__pycache__/serializer.cpython-38.pyc | Bin 0 -> 10837 bytes .../pip/_vendor/html5lib/_ihatexml.py | 288 + .../pip/_vendor/html5lib/_inputstream.py | 923 ++ .../pip/_vendor/html5lib/_tokenizer.py | 1721 ++++ .../pip/_vendor/html5lib/_trie/__init__.py | 14 + .../_trie/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 438 bytes .../_trie/__pycache__/_base.cpython-38.pyc | Bin 0 -> 1617 bytes .../_trie/__pycache__/datrie.cpython-38.pyc | Bin 0 -> 2050 bytes .../_trie/__pycache__/py.cpython-38.pyc | Bin 0 -> 2274 bytes .../pip/_vendor/html5lib/_trie/_base.py | 40 + .../pip/_vendor/html5lib/_trie/datrie.py | 44 + .../pip/_vendor/html5lib/_trie/py.py | 67 + .../pip/_vendor/html5lib/_utils.py | 124 + .../pip/_vendor/html5lib/constants.py | 2947 ++++++ .../pip/_vendor/html5lib/filters/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 213 bytes .../alphabeticalattributes.cpython-38.pyc | Bin 0 -> 1335 bytes .../filters/__pycache__/base.cpython-38.pyc | Bin 0 -> 883 bytes .../inject_meta_charset.cpython-38.pyc | Bin 0 -> 1889 bytes .../filters/__pycache__/lint.cpython-38.pyc | Bin 0 -> 2647 bytes .../__pycache__/optionaltags.cpython-38.pyc | Bin 0 -> 2776 bytes .../__pycache__/sanitizer.cpython-38.pyc | Bin 0 -> 16318 bytes .../__pycache__/whitespace.cpython-38.pyc | Bin 0 -> 1381 bytes .../filters/alphabeticalattributes.py | 29 + .../pip/_vendor/html5lib/filters/base.py | 12 + .../html5lib/filters/inject_meta_charset.py | 73 + .../pip/_vendor/html5lib/filters/lint.py | 93 + .../_vendor/html5lib/filters/optionaltags.py | 207 + .../pip/_vendor/html5lib/filters/sanitizer.py | 896 ++ .../_vendor/html5lib/filters/whitespace.py | 38 + .../pip/_vendor/html5lib/html5parser.py | 2791 ++++++ .../pip/_vendor/html5lib/serializer.py | 409 + .../_vendor/html5lib/treeadapters/__init__.py | 30 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 952 bytes .../__pycache__/genshi.cpython-38.pyc | Bin 0 -> 1558 bytes .../__pycache__/sax.cpython-38.pyc | Bin 0 -> 1493 bytes .../_vendor/html5lib/treeadapters/genshi.py | 54 + .../pip/_vendor/html5lib/treeadapters/sax.py | 50 + .../_vendor/html5lib/treebuilders/__init__.py | 88 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 3337 bytes .../__pycache__/base.cpython-38.pyc | Bin 0 -> 11363 bytes .../__pycache__/dom.cpython-38.pyc | Bin 0 -> 9470 bytes .../__pycache__/etree.cpython-38.pyc | Bin 0 -> 11857 bytes .../__pycache__/etree_lxml.cpython-38.pyc | Bin 0 -> 11825 bytes .../pip/_vendor/html5lib/treebuilders/base.py | 417 + .../pip/_vendor/html5lib/treebuilders/dom.py | 239 + .../_vendor/html5lib/treebuilders/etree.py | 340 + .../html5lib/treebuilders/etree_lxml.py | 366 + .../_vendor/html5lib/treewalkers/__init__.py | 154 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 4018 bytes .../__pycache__/base.cpython-38.pyc | Bin 0 -> 7018 bytes .../__pycache__/dom.cpython-38.pyc | Bin 0 -> 1747 bytes .../__pycache__/etree.cpython-38.pyc | Bin 0 -> 3532 bytes .../__pycache__/etree_lxml.cpython-38.pyc | Bin 0 -> 6631 bytes .../__pycache__/genshi.cpython-38.pyc | Bin 0 -> 1905 bytes .../pip/_vendor/html5lib/treewalkers/base.py | 252 + .../pip/_vendor/html5lib/treewalkers/dom.py | 43 + .../pip/_vendor/html5lib/treewalkers/etree.py | 130 + .../html5lib/treewalkers/etree_lxml.py | 213 + .../_vendor/html5lib/treewalkers/genshi.py | 69 + .../pip/_vendor/idna/__init__.py | 2 + .../idna/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 270 bytes .../idna/__pycache__/codec.cpython-38.pyc | Bin 0 -> 2924 bytes .../idna/__pycache__/compat.cpython-38.pyc | Bin 0 -> 642 bytes .../idna/__pycache__/core.cpython-38.pyc | Bin 0 -> 9058 bytes .../idna/__pycache__/idnadata.cpython-38.pyc | Bin 0 -> 21394 bytes .../idna/__pycache__/intranges.cpython-38.pyc | Bin 0 -> 1822 bytes .../__pycache__/package_data.cpython-38.pyc | Bin 0 -> 224 bytes .../idna/__pycache__/uts46data.cpython-38.pyc | Bin 0 -> 174197 bytes .../site-packages/pip/_vendor/idna/codec.py | 118 + .../site-packages/pip/_vendor/idna/compat.py | 12 + .../site-packages/pip/_vendor/idna/core.py | 396 + .../pip/_vendor/idna/idnadata.py | 1979 ++++ .../pip/_vendor/idna/intranges.py | 53 + .../pip/_vendor/idna/package_data.py | 2 + .../pip/_vendor/idna/uts46data.py | 8205 +++++++++++++++++ .../site-packages/pip/_vendor/ipaddress.py | 2419 +++++ .../pip/_vendor/lockfile/__init__.py | 347 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 9817 bytes .../__pycache__/linklockfile.cpython-38.pyc | Bin 0 -> 2325 bytes .../__pycache__/mkdirlockfile.cpython-38.pyc | Bin 0 -> 2693 bytes .../__pycache__/pidlockfile.cpython-38.pyc | Bin 0 -> 4897 bytes .../__pycache__/sqlitelockfile.cpython-38.pyc | Bin 0 -> 3752 bytes .../symlinklockfile.cpython-38.pyc | Bin 0 -> 2214 bytes .../pip/_vendor/lockfile/linklockfile.py | 73 + .../pip/_vendor/lockfile/mkdirlockfile.py | 84 + .../pip/_vendor/lockfile/pidlockfile.py | 190 + .../pip/_vendor/lockfile/sqlitelockfile.py | 156 + .../pip/_vendor/lockfile/symlinklockfile.py | 70 + .../pip/_vendor/msgpack/__init__.py | 65 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2042 bytes .../__pycache__/_version.cpython-38.pyc | Bin 0 -> 231 bytes .../__pycache__/exceptions.cpython-38.pyc | Bin 0 -> 1865 bytes .../__pycache__/fallback.cpython-38.pyc | Bin 0 -> 26361 bytes .../pip/_vendor/msgpack/_version.py | 1 + .../pip/_vendor/msgpack/exceptions.py | 48 + .../pip/_vendor/msgpack/fallback.py | 1027 +++ .../pip/_vendor/packaging/__about__.py | 27 + .../pip/_vendor/packaging/__init__.py | 26 + .../__pycache__/__about__.cpython-38.pyc | Bin 0 -> 746 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 584 bytes .../__pycache__/_compat.cpython-38.pyc | Bin 0 -> 1020 bytes .../__pycache__/_structures.cpython-38.pyc | Bin 0 -> 2802 bytes .../__pycache__/markers.cpython-38.pyc | Bin 0 -> 8927 bytes .../__pycache__/requirements.cpython-38.pyc | Bin 0 -> 4025 bytes .../__pycache__/specifiers.cpython-38.pyc | Bin 0 -> 19776 bytes .../__pycache__/utils.cpython-38.pyc | Bin 0 -> 1477 bytes .../__pycache__/version.cpython-38.pyc | Bin 0 -> 12107 bytes .../pip/_vendor/packaging/_compat.py | 31 + .../pip/_vendor/packaging/_structures.py | 68 + .../pip/_vendor/packaging/markers.py | 296 + .../pip/_vendor/packaging/requirements.py | 138 + .../pip/_vendor/packaging/specifiers.py | 749 ++ .../pip/_vendor/packaging/utils.py | 57 + .../pip/_vendor/packaging/version.py | 420 + .../pip/_vendor/pep517/__init__.py | 4 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 295 bytes .../__pycache__/_in_process.cpython-38.pyc | Bin 0 -> 5553 bytes .../pep517/__pycache__/build.cpython-38.pyc | Bin 0 -> 2809 bytes .../pep517/__pycache__/check.cpython-38.pyc | Bin 0 -> 4778 bytes .../__pycache__/colorlog.cpython-38.pyc | Bin 0 -> 2965 bytes .../pep517/__pycache__/compat.cpython-38.pyc | Bin 0 -> 1017 bytes .../__pycache__/envbuild.cpython-38.pyc | Bin 0 -> 4297 bytes .../__pycache__/wrappers.cpython-38.pyc | Bin 0 -> 5557 bytes .../pip/_vendor/pep517/_in_process.py | 207 + .../site-packages/pip/_vendor/pep517/build.py | 108 + .../site-packages/pip/_vendor/pep517/check.py | 202 + .../pip/_vendor/pep517/colorlog.py | 115 + .../pip/_vendor/pep517/compat.py | 23 + .../pip/_vendor/pep517/envbuild.py | 158 + .../pip/_vendor/pep517/wrappers.py | 163 + .../pip/_vendor/pkg_resources/__init__.py | 3286 +++++++ .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 100197 bytes .../__pycache__/py31compat.cpython-38.pyc | Bin 0 -> 661 bytes .../pip/_vendor/pkg_resources/py31compat.py | 23 + .../pip/_vendor/progress/__init__.py | 177 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 5630 bytes .../progress/__pycache__/bar.cpython-38.pyc | Bin 0 -> 2654 bytes .../__pycache__/counter.cpython-38.pyc | Bin 0 -> 1486 bytes .../__pycache__/spinner.cpython-38.pyc | Bin 0 -> 1413 bytes .../site-packages/pip/_vendor/progress/bar.py | 91 + .../pip/_vendor/progress/counter.py | 41 + .../pip/_vendor/progress/spinner.py | 43 + .../site-packages/pip/_vendor/pyparsing.py | 6493 +++++++++++++ .../pip/_vendor/pytoml/__init__.py | 4 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 389 bytes .../pytoml/__pycache__/core.cpython-38.pyc | Bin 0 -> 966 bytes .../pytoml/__pycache__/parser.cpython-38.pyc | Bin 0 -> 10163 bytes .../pytoml/__pycache__/test.cpython-38.pyc | Bin 0 -> 1268 bytes .../pytoml/__pycache__/utils.cpython-38.pyc | Bin 0 -> 2178 bytes .../pytoml/__pycache__/writer.cpython-38.pyc | Bin 0 -> 3609 bytes .../site-packages/pip/_vendor/pytoml/core.py | 13 + .../pip/_vendor/pytoml/parser.py | 341 + .../site-packages/pip/_vendor/pytoml/test.py | 30 + .../site-packages/pip/_vendor/pytoml/utils.py | 67 + .../pip/_vendor/pytoml/writer.py | 106 + .../pip/_vendor/requests/__init__.py | 133 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 3516 bytes .../__pycache__/__version__.cpython-38.pyc | Bin 0 -> 563 bytes .../_internal_utils.cpython-38.pyc | Bin 0 -> 1333 bytes .../__pycache__/adapters.cpython-38.pyc | Bin 0 -> 16996 bytes .../requests/__pycache__/api.cpython-38.pyc | Bin 0 -> 6520 bytes .../requests/__pycache__/auth.cpython-38.pyc | Bin 0 -> 8342 bytes .../requests/__pycache__/certs.cpython-38.pyc | Bin 0 -> 646 bytes .../__pycache__/compat.cpython-38.pyc | Bin 0 -> 1625 bytes .../__pycache__/cookies.cpython-38.pyc | Bin 0 -> 18847 bytes .../__pycache__/exceptions.cpython-38.pyc | Bin 0 -> 5257 bytes .../requests/__pycache__/help.cpython-38.pyc | Bin 0 -> 2743 bytes .../requests/__pycache__/hooks.cpython-38.pyc | Bin 0 -> 1005 bytes .../__pycache__/models.cpython-38.pyc | Bin 0 -> 23941 bytes .../__pycache__/packages.cpython-38.pyc | Bin 0 -> 515 bytes .../__pycache__/sessions.cpython-38.pyc | Bin 0 -> 19555 bytes .../__pycache__/status_codes.cpython-38.pyc | Bin 0 -> 4201 bytes .../__pycache__/structures.cpython-38.pyc | Bin 0 -> 4443 bytes .../requests/__pycache__/utils.cpython-38.pyc | Bin 0 -> 22212 bytes .../pip/_vendor/requests/__version__.py | 14 + .../pip/_vendor/requests/_internal_utils.py | 42 + .../pip/_vendor/requests/adapters.py | 533 ++ .../site-packages/pip/_vendor/requests/api.py | 158 + .../pip/_vendor/requests/auth.py | 305 + .../pip/_vendor/requests/certs.py | 18 + .../pip/_vendor/requests/compat.py | 74 + .../pip/_vendor/requests/cookies.py | 549 ++ .../pip/_vendor/requests/exceptions.py | 126 + .../pip/_vendor/requests/help.py | 119 + .../pip/_vendor/requests/hooks.py | 34 + .../pip/_vendor/requests/models.py | 953 ++ .../pip/_vendor/requests/packages.py | 16 + .../pip/_vendor/requests/sessions.py | 770 ++ .../pip/_vendor/requests/status_codes.py | 120 + .../pip/_vendor/requests/structures.py | 103 + .../pip/_vendor/requests/utils.py | 977 ++ .../site-packages/pip/_vendor/retrying.py | 267 + .../site-packages/pip/_vendor/six.py | 952 ++ .../pip/_vendor/urllib3/__init__.py | 91 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2138 bytes .../__pycache__/_collections.cpython-38.pyc | Bin 0 -> 10788 bytes .../__pycache__/connection.cpython-38.pyc | Bin 0 -> 10579 bytes .../__pycache__/connectionpool.cpython-38.pyc | Bin 0 -> 23893 bytes .../__pycache__/exceptions.cpython-38.pyc | Bin 0 -> 10081 bytes .../urllib3/__pycache__/fields.cpython-38.pyc | Bin 0 -> 8146 bytes .../__pycache__/filepost.cpython-38.pyc | Bin 0 -> 2783 bytes .../__pycache__/poolmanager.cpython-38.pyc | Bin 0 -> 12959 bytes .../__pycache__/request.cpython-38.pyc | Bin 0 -> 5637 bytes .../__pycache__/response.cpython-38.pyc | Bin 0 -> 20148 bytes .../pip/_vendor/urllib3/_collections.py | 329 + .../pip/_vendor/urllib3/connection.py | 417 + .../pip/_vendor/urllib3/connectionpool.py | 897 ++ .../pip/_vendor/urllib3/contrib/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 212 bytes .../_appengine_environ.cpython-38.pyc | Bin 0 -> 1140 bytes .../__pycache__/appengine.cpython-38.pyc | Bin 0 -> 8453 bytes .../__pycache__/ntlmpool.cpython-38.pyc | Bin 0 -> 3279 bytes .../__pycache__/pyopenssl.cpython-38.pyc | Bin 0 -> 14984 bytes .../securetransport.cpython-38.pyc | Bin 0 -> 19912 bytes .../contrib/__pycache__/socks.cpython-38.pyc | Bin 0 -> 5598 bytes .../urllib3/contrib/_appengine_environ.py | 30 + .../contrib/_securetransport/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 229 bytes .../__pycache__/bindings.cpython-38.pyc | Bin 0 -> 10384 bytes .../__pycache__/low_level.cpython-38.pyc | Bin 0 -> 7581 bytes .../contrib/_securetransport/bindings.py | 593 ++ .../contrib/_securetransport/low_level.py | 346 + .../pip/_vendor/urllib3/contrib/appengine.py | 289 + .../pip/_vendor/urllib3/contrib/ntlmpool.py | 111 + .../pip/_vendor/urllib3/contrib/pyopenssl.py | 485 + .../urllib3/contrib/securetransport.py | 853 ++ .../pip/_vendor/urllib3/contrib/socks.py | 205 + .../pip/_vendor/urllib3/exceptions.py | 246 + .../pip/_vendor/urllib3/fields.py | 272 + .../pip/_vendor/urllib3/filepost.py | 98 + .../pip/_vendor/urllib3/packages/__init__.py | 5 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 326 bytes .../packages/__pycache__/six.cpython-38.pyc | Bin 0 -> 24486 bytes .../urllib3/packages/backports/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 223 bytes .../__pycache__/makefile.cpython-38.pyc | Bin 0 -> 1327 bytes .../urllib3/packages/backports/makefile.py | 53 + .../urllib3/packages/rfc3986/__init__.py | 56 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1060 bytes .../rfc3986/__pycache__/_mixin.cpython-38.pyc | Bin 0 -> 10661 bytes .../__pycache__/abnf_regexp.cpython-38.pyc | Bin 0 -> 4164 bytes .../rfc3986/__pycache__/api.cpython-38.pyc | Bin 0 -> 3725 bytes .../__pycache__/builder.cpython-38.pyc | Bin 0 -> 8167 bytes .../rfc3986/__pycache__/compat.cpython-38.pyc | Bin 0 -> 1162 bytes .../__pycache__/exceptions.cpython-38.pyc | Bin 0 -> 4801 bytes .../rfc3986/__pycache__/iri.cpython-38.pyc | Bin 0 -> 4516 bytes .../rfc3986/__pycache__/misc.cpython-38.pyc | Bin 0 -> 2279 bytes .../__pycache__/normalizers.cpython-38.pyc | Bin 0 -> 3594 bytes .../__pycache__/parseresult.cpython-38.pyc | Bin 0 -> 9908 bytes .../rfc3986/__pycache__/uri.cpython-38.pyc | Bin 0 -> 4289 bytes .../__pycache__/validators.cpython-38.pyc | Bin 0 -> 13049 bytes .../urllib3/packages/rfc3986/_mixin.py | 353 + .../urllib3/packages/rfc3986/abnf_regexp.py | 267 + .../_vendor/urllib3/packages/rfc3986/api.py | 106 + .../urllib3/packages/rfc3986/builder.py | 298 + .../urllib3/packages/rfc3986/compat.py | 54 + .../urllib3/packages/rfc3986/exceptions.py | 118 + .../_vendor/urllib3/packages/rfc3986/iri.py | 147 + .../_vendor/urllib3/packages/rfc3986/misc.py | 124 + .../urllib3/packages/rfc3986/normalizers.py | 167 + .../urllib3/packages/rfc3986/parseresult.py | 385 + .../_vendor/urllib3/packages/rfc3986/uri.py | 153 + .../urllib3/packages/rfc3986/validators.py | 450 + .../pip/_vendor/urllib3/packages/six.py | 868 ++ .../packages/ssl_match_hostname/__init__.py | 19 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 567 bytes .../_implementation.cpython-38.pyc | Bin 0 -> 3340 bytes .../ssl_match_hostname/_implementation.py | 156 + .../pip/_vendor/urllib3/poolmanager.py | 455 + .../pip/_vendor/urllib3/request.py | 150 + .../pip/_vendor/urllib3/response.py | 760 ++ .../pip/_vendor/urllib3/util/__init__.py | 56 + .../util/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1032 bytes .../__pycache__/connection.cpython-38.pyc | Bin 0 -> 3205 bytes .../util/__pycache__/queue.cpython-38.pyc | Bin 0 -> 1077 bytes .../util/__pycache__/request.cpython-38.pyc | Bin 0 -> 3359 bytes .../util/__pycache__/response.cpython-38.pyc | Bin 0 -> 2002 bytes .../util/__pycache__/retry.cpython-38.pyc | Bin 0 -> 12953 bytes .../util/__pycache__/ssl_.cpython-38.pyc | Bin 0 -> 9831 bytes .../util/__pycache__/timeout.cpython-38.pyc | Bin 0 -> 8833 bytes .../util/__pycache__/url.cpython-38.pyc | Bin 0 -> 7898 bytes .../util/__pycache__/wait.cpython-38.pyc | Bin 0 -> 3126 bytes .../pip/_vendor/urllib3/util/connection.py | 134 + .../pip/_vendor/urllib3/util/queue.py | 21 + .../pip/_vendor/urllib3/util/request.py | 125 + .../pip/_vendor/urllib3/util/response.py | 87 + .../pip/_vendor/urllib3/util/retry.py | 412 + .../pip/_vendor/urllib3/util/ssl_.py | 392 + .../pip/_vendor/urllib3/util/timeout.py | 243 + .../pip/_vendor/urllib3/util/url.py | 289 + .../pip/_vendor/urllib3/util/wait.py | 150 + .../pip/_vendor/webencodings/__init__.py | 342 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 9752 bytes .../__pycache__/labels.cpython-38.pyc | Bin 0 -> 3850 bytes .../__pycache__/mklabels.cpython-38.pyc | Bin 0 -> 1946 bytes .../__pycache__/tests.cpython-38.pyc | Bin 0 -> 5112 bytes .../__pycache__/x_user_defined.cpython-38.pyc | Bin 0 -> 2680 bytes .../pip/_vendor/webencodings/labels.py | 231 + .../pip/_vendor/webencodings/mklabels.py | 59 + .../pip/_vendor/webencodings/tests.py | 153 + .../_vendor/webencodings/x_user_defined.py | 325 + .../site-packages/pkg_resources/__init__.py | 3295 +++++++ .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 100394 bytes .../__pycache__/py31compat.cpython-38.pyc | Bin 0 -> 656 bytes .../pkg_resources/_vendor/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 213 bytes .../__pycache__/appdirs.cpython-38.pyc | Bin 0 -> 20566 bytes .../__pycache__/pyparsing.cpython-38.pyc | Bin 0 -> 201690 bytes .../_vendor/__pycache__/six.cpython-38.pyc | Bin 0 -> 24486 bytes .../pkg_resources/_vendor/appdirs.py | 608 ++ .../_vendor/packaging/__about__.py | 21 + .../_vendor/packaging/__init__.py | 14 + .../__pycache__/__about__.cpython-38.pyc | Bin 0 -> 763 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 601 bytes .../__pycache__/_compat.cpython-38.pyc | Bin 0 -> 1037 bytes .../__pycache__/_structures.cpython-38.pyc | Bin 0 -> 2819 bytes .../__pycache__/markers.cpython-38.pyc | Bin 0 -> 8975 bytes .../__pycache__/requirements.cpython-38.pyc | Bin 0 -> 3934 bytes .../__pycache__/specifiers.cpython-38.pyc | Bin 0 -> 19843 bytes .../__pycache__/utils.cpython-38.pyc | Bin 0 -> 522 bytes .../__pycache__/version.cpython-38.pyc | Bin 0 -> 10690 bytes .../_vendor/packaging/_compat.py | 30 + .../_vendor/packaging/_structures.py | 68 + .../_vendor/packaging/markers.py | 301 + .../_vendor/packaging/requirements.py | 127 + .../_vendor/packaging/specifiers.py | 774 ++ .../pkg_resources/_vendor/packaging/utils.py | 14 + .../_vendor/packaging/version.py | 393 + .../pkg_resources/_vendor/pyparsing.py | 5742 ++++++++++++ .../pkg_resources/_vendor/six.py | 868 ++ .../pkg_resources/extern/__init__.py | 73 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2462 bytes .../site-packages/pkg_resources/py31compat.py | 23 + .../protobuf-3.19.1-py3.8-nspkg.pth | 1 + .../protobuf-3.19.1.dist-info/INSTALLER | 1 + .../protobuf-3.19.1.dist-info/METADATA | 22 + .../protobuf-3.19.1.dist-info/RECORD | 97 + .../protobuf-3.19.1.dist-info/WHEEL | 5 + .../namespace_packages.txt | 1 + .../protobuf-3.19.1.dist-info/top_level.txt | 1 + .../pyparsing-2.4.7.dist-info/INSTALLER | 1 + .../pyparsing-2.4.7.dist-info/LICENSE | 18 + .../pyparsing-2.4.7.dist-info/METADATA | 104 + .../pyparsing-2.4.7.dist-info/RECORD | 8 + .../pyparsing-2.4.7.dist-info/WHEEL | 6 + .../pyparsing-2.4.7.dist-info/top_level.txt | 1 + .../lib/python3.8/site-packages/pyparsing.py | 7107 ++++++++++++++ .../setuptools-41.2.0.dist-info/INSTALLER | 1 + .../setuptools-41.2.0.dist-info/LICENSE | 19 + .../setuptools-41.2.0.dist-info/METADATA | 77 + .../setuptools-41.2.0.dist-info/RECORD | 186 + .../setuptools-41.2.0.dist-info/WHEEL | 6 + .../dependency_links.txt | 2 + .../entry_points.txt | 65 + .../setuptools-41.2.0.dist-info/top_level.txt | 3 + .../setuptools-41.2.0.dist-info/zip-safe | 1 + .../site-packages/setuptools/__init__.py | 228 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 7824 bytes .../_deprecation_warning.cpython-38.pyc | Bin 0 -> 572 bytes .../__pycache__/archive_util.cpython-38.pyc | Bin 0 -> 5184 bytes .../__pycache__/build_meta.cpython-38.pyc | Bin 0 -> 8551 bytes .../__pycache__/config.cpython-38.pyc | Bin 0 -> 17832 bytes .../__pycache__/dep_util.cpython-38.pyc | Bin 0 -> 875 bytes .../__pycache__/depends.cpython-38.pyc | Bin 0 -> 5344 bytes .../__pycache__/dist.cpython-38.pyc | Bin 0 -> 42203 bytes .../__pycache__/extension.cpython-38.pyc | Bin 0 -> 2017 bytes .../__pycache__/glibc.cpython-38.pyc | Bin 0 -> 1586 bytes .../__pycache__/glob.cpython-38.pyc | Bin 0 -> 3789 bytes .../__pycache__/launch.cpython-38.pyc | Bin 0 -> 880 bytes .../__pycache__/lib2to3_ex.cpython-38.pyc | Bin 0 -> 2467 bytes .../__pycache__/monkey.cpython-38.pyc | Bin 0 -> 4700 bytes .../__pycache__/msvc.cpython-38.pyc | Bin 0 -> 34496 bytes .../__pycache__/namespaces.cpython-38.pyc | Bin 0 -> 3672 bytes .../__pycache__/package_index.cpython-38.pyc | Bin 0 -> 33017 bytes .../__pycache__/pep425tags.cpython-38.pyc | Bin 0 -> 7259 bytes .../__pycache__/py27compat.cpython-38.pyc | Bin 0 -> 840 bytes .../__pycache__/py31compat.cpython-38.pyc | Bin 0 -> 1247 bytes .../__pycache__/py33compat.cpython-38.pyc | Bin 0 -> 1464 bytes .../__pycache__/sandbox.cpython-38.pyc | Bin 0 -> 15592 bytes .../__pycache__/site-patch.cpython-38.pyc | Bin 0 -> 1530 bytes .../__pycache__/ssl_support.cpython-38.pyc | Bin 0 -> 6911 bytes .../__pycache__/unicode_utils.cpython-38.pyc | Bin 0 -> 1207 bytes .../__pycache__/version.cpython-38.pyc | Bin 0 -> 348 bytes .../__pycache__/wheel.cpython-38.pyc | Bin 0 -> 7110 bytes .../windows_support.cpython-38.pyc | Bin 0 -> 1045 bytes .../setuptools/_deprecation_warning.py | 7 + .../setuptools/_vendor/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 210 bytes .../__pycache__/pyparsing.cpython-38.pyc | Bin 0 -> 201687 bytes .../_vendor/__pycache__/six.cpython-38.pyc | Bin 0 -> 24483 bytes .../setuptools/_vendor/packaging/__about__.py | 21 + .../setuptools/_vendor/packaging/__init__.py | 14 + .../__pycache__/__about__.cpython-38.pyc | Bin 0 -> 760 bytes .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 598 bytes .../__pycache__/_compat.cpython-38.pyc | Bin 0 -> 1034 bytes .../__pycache__/_structures.cpython-38.pyc | Bin 0 -> 2816 bytes .../__pycache__/markers.cpython-38.pyc | Bin 0 -> 8969 bytes .../__pycache__/requirements.cpython-38.pyc | Bin 0 -> 3925 bytes .../__pycache__/specifiers.cpython-38.pyc | Bin 0 -> 19840 bytes .../__pycache__/utils.cpython-38.pyc | Bin 0 -> 519 bytes .../__pycache__/version.cpython-38.pyc | Bin 0 -> 10687 bytes .../setuptools/_vendor/packaging/_compat.py | 30 + .../_vendor/packaging/_structures.py | 68 + .../setuptools/_vendor/packaging/markers.py | 301 + .../_vendor/packaging/requirements.py | 127 + .../_vendor/packaging/specifiers.py | 774 ++ .../setuptools/_vendor/packaging/utils.py | 14 + .../setuptools/_vendor/packaging/version.py | 393 + .../setuptools/_vendor/pyparsing.py | 5742 ++++++++++++ .../site-packages/setuptools/_vendor/six.py | 868 ++ .../site-packages/setuptools/archive_util.py | 173 + .../site-packages/setuptools/build_meta.py | 257 + .../site-packages/setuptools/cli-32.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/cli-64.exe | Bin 0 -> 74752 bytes .../site-packages/setuptools/cli.exe | Bin 0 -> 65536 bytes .../setuptools/command/__init__.py | 18 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 794 bytes .../command/__pycache__/alias.cpython-38.pyc | Bin 0 -> 2448 bytes .../__pycache__/bdist_egg.cpython-38.pyc | Bin 0 -> 14234 bytes .../__pycache__/bdist_rpm.cpython-38.pyc | Bin 0 -> 1840 bytes .../__pycache__/bdist_wininst.cpython-38.pyc | Bin 0 -> 1010 bytes .../__pycache__/build_clib.cpython-38.pyc | Bin 0 -> 2493 bytes .../__pycache__/build_ext.cpython-38.pyc | Bin 0 -> 9951 bytes .../__pycache__/build_py.cpython-38.pyc | Bin 0 -> 8697 bytes .../__pycache__/develop.cpython-38.pyc | Bin 0 -> 6557 bytes .../__pycache__/dist_info.cpython-38.pyc | Bin 0 -> 1417 bytes .../__pycache__/easy_install.cpython-38.pyc | Bin 0 -> 65318 bytes .../__pycache__/egg_info.cpython-38.pyc | Bin 0 -> 21820 bytes .../__pycache__/install.cpython-38.pyc | Bin 0 -> 4047 bytes .../install_egg_info.cpython-38.pyc | Bin 0 -> 2459 bytes .../__pycache__/install_lib.cpython-38.pyc | Bin 0 -> 4180 bytes .../install_scripts.cpython-38.pyc | Bin 0 -> 2329 bytes .../__pycache__/py36compat.cpython-38.pyc | Bin 0 -> 4666 bytes .../__pycache__/register.cpython-38.pyc | Bin 0 -> 811 bytes .../command/__pycache__/rotate.cpython-38.pyc | Bin 0 -> 2576 bytes .../__pycache__/saveopts.cpython-38.pyc | Bin 0 -> 945 bytes .../command/__pycache__/sdist.cpython-38.pyc | Bin 0 -> 6863 bytes .../command/__pycache__/setopt.cpython-38.pyc | Bin 0 -> 4593 bytes .../command/__pycache__/test.cpython-38.pyc | Bin 0 -> 8296 bytes .../command/__pycache__/upload.cpython-38.pyc | Bin 0 -> 5259 bytes .../__pycache__/upload_docs.cpython-38.pyc | Bin 0 -> 6196 bytes .../site-packages/setuptools/command/alias.py | 80 + .../setuptools/command/bdist_egg.py | 502 + .../setuptools/command/bdist_rpm.py | 43 + .../setuptools/command/bdist_wininst.py | 21 + .../setuptools/command/build_clib.py | 98 + .../setuptools/command/build_ext.py | 327 + .../setuptools/command/build_py.py | 270 + .../setuptools/command/develop.py | 221 + .../setuptools/command/dist_info.py | 36 + .../setuptools/command/easy_install.py | 2342 +++++ .../setuptools/command/egg_info.py | 717 ++ .../setuptools/command/install.py | 125 + .../setuptools/command/install_egg_info.py | 62 + .../setuptools/command/install_lib.py | 121 + .../setuptools/command/install_scripts.py | 65 + .../setuptools/command/launcher manifest.xml | 15 + .../setuptools/command/py36compat.py | 136 + .../setuptools/command/register.py | 18 + .../setuptools/command/rotate.py | 66 + .../setuptools/command/saveopts.py | 22 + .../site-packages/setuptools/command/sdist.py | 221 + .../setuptools/command/setopt.py | 149 + .../site-packages/setuptools/command/test.py | 271 + .../setuptools/command/upload.py | 196 + .../setuptools/command/upload_docs.py | 206 + .../site-packages/setuptools/config.py | 656 ++ .../site-packages/setuptools/dep_util.py | 23 + .../site-packages/setuptools/depends.py | 186 + .../site-packages/setuptools/dist.py | 1280 +++ .../site-packages/setuptools/extension.py | 57 + .../setuptools/extern/__init__.py | 73 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2463 bytes .../site-packages/setuptools/glibc.py | 86 + .../site-packages/setuptools/glob.py | 174 + .../site-packages/setuptools/gui-32.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/gui-64.exe | Bin 0 -> 75264 bytes .../site-packages/setuptools/gui.exe | Bin 0 -> 65536 bytes .../site-packages/setuptools/launch.py | 35 + .../site-packages/setuptools/lib2to3_ex.py | 62 + .../site-packages/setuptools/monkey.py | 179 + .../site-packages/setuptools/msvc.py | 1301 +++ .../site-packages/setuptools/namespaces.py | 107 + .../site-packages/setuptools/package_index.py | 1136 +++ .../site-packages/setuptools/pep425tags.py | 319 + .../site-packages/setuptools/py27compat.py | 28 + .../site-packages/setuptools/py31compat.py | 32 + .../site-packages/setuptools/py33compat.py | 59 + .../site-packages/setuptools/sandbox.py | 491 + .../setuptools/script (dev).tmpl | 6 + .../site-packages/setuptools/script.tmpl | 3 + .../site-packages/setuptools/site-patch.py | 74 + .../site-packages/setuptools/ssl_support.py | 260 + .../site-packages/setuptools/unicode_utils.py | 44 + .../site-packages/setuptools/version.py | 6 + .../site-packages/setuptools/wheel.py | 211 + .../setuptools/windows_support.py | 29 + .../six-1.16.0.dist-info/INSTALLER | 1 + .../six-1.16.0.dist-info/LICENSE | 18 + .../six-1.16.0.dist-info/METADATA | 49 + .../site-packages/six-1.16.0.dist-info/RECORD | 8 + .../site-packages/six-1.16.0.dist-info/WHEEL | 6 + .../six-1.16.0.dist-info/top_level.txt | 1 + .../venv/lib/python3.8/site-packages/six.py | 998 ++ .../site-packages/sqlalchemy/__init__.py | 158 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 4771 bytes .../__pycache__/events.cpython-38.pyc | Bin 0 -> 469 bytes .../sqlalchemy/__pycache__/exc.cpython-38.pyc | Bin 0 -> 21832 bytes .../__pycache__/inspection.cpython-38.pyc | Bin 0 -> 2973 bytes .../sqlalchemy/__pycache__/log.cpython-38.pyc | Bin 0 -> 6743 bytes .../__pycache__/processors.cpython-38.pyc | Bin 0 -> 4318 bytes .../__pycache__/schema.cpython-38.pyc | Bin 0 -> 2025 bytes .../__pycache__/types.cpython-38.pyc | Bin 0 -> 2433 bytes .../cimmutabledict.cpython-38-darwin.so | Bin 0 -> 37864 bytes .../sqlalchemy/connectors/__init__.py | 10 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 374 bytes .../__pycache__/mxodbc.cpython-38.pyc | Bin 0 -> 5530 bytes .../__pycache__/pyodbc.cpython-38.pyc | Bin 0 -> 5183 bytes .../sqlalchemy/connectors/mxodbc.py | 166 + .../sqlalchemy/connectors/pyodbc.py | 193 + .../cprocessors.cpython-38-darwin.so | Bin 0 -> 37272 bytes .../cresultproxy.cpython-38-darwin.so | Bin 0 -> 41376 bytes .../sqlalchemy/databases/__init__.py | 38 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 874 bytes .../sqlalchemy/dialects/__init__.py | 72 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1378 bytes .../sqlalchemy/dialects/firebird/__init__.py | 41 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 774 bytes .../firebird/__pycache__/base.cpython-38.pyc | Bin 0 -> 26866 bytes .../firebird/__pycache__/fdb.cpython-38.pyc | Bin 0 -> 4131 bytes .../__pycache__/kinterbasdb.cpython-38.pyc | Bin 0 -> 6558 bytes .../sqlalchemy/dialects/firebird/base.py | 989 ++ .../sqlalchemy/dialects/firebird/fdb.py | 112 + .../dialects/firebird/kinterbasdb.py | 202 + .../sqlalchemy/dialects/mssql/__init__.py | 85 + .../mssql/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1538 bytes .../mssql/__pycache__/base.cpython-38.pyc | Bin 0 -> 97278 bytes .../information_schema.cpython-38.pyc | Bin 0 -> 5430 bytes .../mssql/__pycache__/json.cpython-38.pyc | Bin 0 -> 4793 bytes .../mssql/__pycache__/mxodbc.cpython-38.pyc | Bin 0 -> 5168 bytes .../__pycache__/provision.cpython-38.pyc | Bin 0 -> 3616 bytes .../mssql/__pycache__/pymssql.cpython-38.pyc | Bin 0 -> 5228 bytes .../mssql/__pycache__/pyodbc.cpython-38.pyc | Bin 0 -> 20634 bytes .../sqlalchemy/dialects/mssql/base.py | 3505 +++++++ .../dialects/mssql/information_schema.py | 232 + .../sqlalchemy/dialects/mssql/json.py | 125 + .../sqlalchemy/dialects/mssql/mxodbc.py | 150 + .../sqlalchemy/dialects/mssql/provision.py | 116 + .../sqlalchemy/dialects/mssql/pymssql.py | 138 + .../sqlalchemy/dialects/mssql/pyodbc.py | 618 ++ .../sqlalchemy/dialects/mysql/__init__.py | 103 + .../mysql/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1927 bytes .../mysql/__pycache__/aiomysql.cpython-38.pyc | Bin 0 -> 10706 bytes .../mysql/__pycache__/asyncmy.cpython-38.pyc | Bin 0 -> 11102 bytes .../mysql/__pycache__/base.cpython-38.pyc | Bin 0 -> 97082 bytes .../mysql/__pycache__/cymysql.cpython-38.pyc | Bin 0 -> 2612 bytes .../mysql/__pycache__/dml.cpython-38.pyc | Bin 0 -> 6055 bytes .../__pycache__/enumerated.cpython-38.pyc | Bin 0 -> 8477 bytes .../__pycache__/expression.cpython-38.pyc | Bin 0 -> 3908 bytes .../mysql/__pycache__/json.cpython-38.pyc | Bin 0 -> 3035 bytes .../mysql/__pycache__/mariadb.cpython-38.pyc | Bin 0 -> 867 bytes .../mariadbconnector.cpython-38.pyc | Bin 0 -> 6900 bytes .../__pycache__/mysqlconnector.cpython-38.pyc | Bin 0 -> 8185 bytes .../mysql/__pycache__/mysqldb.cpython-38.pyc | Bin 0 -> 9527 bytes .../mysql/__pycache__/oursql.cpython-38.pyc | Bin 0 -> 8188 bytes .../__pycache__/provision.cpython-38.pyc | Bin 0 -> 2062 bytes .../mysql/__pycache__/pymysql.cpython-38.pyc | Bin 0 -> 2824 bytes .../mysql/__pycache__/pyodbc.cpython-38.pyc | Bin 0 -> 4501 bytes .../__pycache__/reflection.cpython-38.pyc | Bin 0 -> 12881 bytes .../__pycache__/reserved_words.cpython-38.pyc | Bin 0 -> 5371 bytes .../mysql/__pycache__/types.cpython-38.pyc | Bin 0 -> 26900 bytes .../sqlalchemy/dialects/mysql/aiomysql.py | 317 + .../sqlalchemy/dialects/mysql/asyncmy.py | 329 + .../sqlalchemy/dialects/mysql/base.py | 3281 +++++++ .../sqlalchemy/dialects/mysql/cymysql.py | 82 + .../sqlalchemy/dialects/mysql/dml.py | 173 + .../sqlalchemy/dialects/mysql/enumerated.py | 254 + .../sqlalchemy/dialects/mysql/expression.py | 130 + .../sqlalchemy/dialects/mysql/json.py | 84 + .../sqlalchemy/dialects/mysql/mariadb.py | 25 + .../dialects/mysql/mariadbconnector.py | 231 + .../dialects/mysql/mysqlconnector.py | 240 + .../sqlalchemy/dialects/mysql/mysqldb.py | 331 + .../sqlalchemy/dialects/mysql/oursql.py | 273 + .../sqlalchemy/dialects/mysql/provision.py | 78 + .../sqlalchemy/dialects/mysql/pymysql.py | 98 + .../sqlalchemy/dialects/mysql/pyodbc.py | 137 + .../sqlalchemy/dialects/mysql/reflection.py | 558 ++ .../dialects/mysql/reserved_words.py | 564 ++ .../sqlalchemy/dialects/mysql/types.py | 773 ++ .../sqlalchemy/dialects/oracle/__init__.py | 58 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1056 bytes .../oracle/__pycache__/base.cpython-38.pyc | Bin 0 -> 73616 bytes .../__pycache__/cx_oracle.cpython-38.pyc | Bin 0 -> 46126 bytes .../__pycache__/provision.cpython-38.pyc | Bin 0 -> 4891 bytes .../sqlalchemy/dialects/oracle/base.py | 2510 +++++ .../sqlalchemy/dialects/oracle/cx_oracle.py | 1408 +++ .../sqlalchemy/dialects/oracle/provision.py | 160 + .../dialects/postgresql/__init__.py | 117 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2208 bytes .../__pycache__/array.cpython-38.pyc | Bin 0 -> 12214 bytes .../__pycache__/asyncpg.cpython-38.pyc | Bin 0 -> 36211 bytes .../__pycache__/base.cpython-38.pyc | Bin 0 -> 134699 bytes .../postgresql/__pycache__/dml.cpython-38.pyc | Bin 0 -> 8381 bytes .../postgresql/__pycache__/ext.cpython-38.pyc | Bin 0 -> 8414 bytes .../__pycache__/hstore.cpython-38.pyc | Bin 0 -> 12620 bytes .../__pycache__/json.cpython-38.pyc | Bin 0 -> 10814 bytes .../__pycache__/pg8000.cpython-38.pyc | Bin 0 -> 20906 bytes .../__pycache__/provision.cpython-38.pyc | Bin 0 -> 3877 bytes .../__pycache__/psycopg2.cpython-38.pyc | Bin 0 -> 32985 bytes .../__pycache__/psycopg2cffi.cpython-38.pyc | Bin 0 -> 1823 bytes .../__pycache__/pygresql.cpython-38.pyc | Bin 0 -> 8377 bytes .../__pycache__/pypostgresql.cpython-38.pyc | Bin 0 -> 4111 bytes .../__pycache__/ranges.cpython-38.pyc | Bin 0 -> 5759 bytes .../sqlalchemy/dialects/postgresql/array.py | 386 + .../sqlalchemy/dialects/postgresql/asyncpg.py | 1088 +++ .../sqlalchemy/dialects/postgresql/base.py | 4496 +++++++++ .../sqlalchemy/dialects/postgresql/dml.py | 273 + .../sqlalchemy/dialects/postgresql/ext.py | 275 + .../sqlalchemy/dialects/postgresql/hstore.py | 447 + .../sqlalchemy/dialects/postgresql/json.py | 327 + .../sqlalchemy/dialects/postgresql/pg8000.py | 594 ++ .../dialects/postgresql/provision.py | 127 + .../dialects/postgresql/psycopg2.py | 1050 +++ .../dialects/postgresql/psycopg2cffi.py | 60 + .../dialects/postgresql/pygresql.py | 278 + .../dialects/postgresql/pypostgresql.py | 126 + .../sqlalchemy/dialects/postgresql/ranges.py | 138 + .../sqlalchemy/dialects/sqlite/__init__.py | 58 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1059 bytes .../__pycache__/aiosqlite.cpython-38.pyc | Bin 0 -> 10575 bytes .../sqlite/__pycache__/base.cpython-38.pyc | Bin 0 -> 72034 bytes .../sqlite/__pycache__/dml.cpython-38.pyc | Bin 0 -> 6805 bytes .../sqlite/__pycache__/json.cpython-38.pyc | Bin 0 -> 3284 bytes .../__pycache__/provision.cpython-38.pyc | Bin 0 -> 4120 bytes .../__pycache__/pysqlcipher.cpython-38.pyc | Bin 0 -> 5607 bytes .../__pycache__/pysqlite.cpython-38.pyc | Bin 0 -> 22430 bytes .../sqlalchemy/dialects/sqlite/aiosqlite.py | 336 + .../sqlalchemy/dialects/sqlite/base.py | 2551 +++++ .../sqlalchemy/dialects/sqlite/dml.py | 199 + .../sqlalchemy/dialects/sqlite/json.py | 84 + .../sqlalchemy/dialects/sqlite/provision.py | 142 + .../sqlalchemy/dialects/sqlite/pysqlcipher.py | 164 + .../sqlalchemy/dialects/sqlite/pysqlite.py | 613 ++ .../sqlalchemy/dialects/sybase/__init__.py | 67 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1173 bytes .../sybase/__pycache__/base.cpython-38.pyc | Bin 0 -> 28722 bytes .../sybase/__pycache__/mxodbc.cpython-38.pyc | Bin 0 -> 1078 bytes .../sybase/__pycache__/pyodbc.cpython-38.pyc | Bin 0 -> 2643 bytes .../__pycache__/pysybase.cpython-38.pyc | Bin 0 -> 3801 bytes .../sqlalchemy/dialects/sybase/base.py | 1100 +++ .../sqlalchemy/dialects/sybase/mxodbc.py | 34 + .../sqlalchemy/dialects/sybase/pyodbc.py | 89 + .../sqlalchemy/dialects/sybase/pysybase.py | 106 + .../sqlalchemy/engine/__init__.py | 61 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2238 bytes .../engine/__pycache__/base.cpython-38.pyc | Bin 0 -> 96257 bytes .../characteristics.cpython-38.pyc | Bin 0 -> 2707 bytes .../engine/__pycache__/create.cpython-38.pyc | Bin 0 -> 28363 bytes .../engine/__pycache__/cursor.cpython-38.pyc | Bin 0 -> 56390 bytes .../engine/__pycache__/default.cpython-38.pyc | Bin 0 -> 45596 bytes .../engine/__pycache__/events.cpython-38.pyc | Bin 0 -> 34093 bytes .../__pycache__/interfaces.cpython-38.pyc | Bin 0 -> 59850 bytes .../engine/__pycache__/mock.cpython-38.pyc | Bin 0 -> 4338 bytes .../__pycache__/reflection.cpython-38.pyc | Bin 0 -> 31715 bytes .../engine/__pycache__/result.cpython-38.pyc | Bin 0 -> 55120 bytes .../engine/__pycache__/row.cpython-38.pyc | Bin 0 -> 18994 bytes .../__pycache__/strategies.cpython-38.pyc | Bin 0 -> 506 bytes .../engine/__pycache__/url.cpython-38.pyc | Bin 0 -> 24377 bytes .../engine/__pycache__/util.cpython-38.pyc | Bin 0 -> 4701 bytes .../site-packages/sqlalchemy/engine/base.py | 3332 +++++++ .../sqlalchemy/engine/characteristics.py | 56 + .../site-packages/sqlalchemy/engine/create.py | 755 ++ .../site-packages/sqlalchemy/engine/cursor.py | 1940 ++++ .../sqlalchemy/engine/default.py | 1892 ++++ .../site-packages/sqlalchemy/engine/events.py | 829 ++ .../sqlalchemy/engine/interfaces.py | 1756 ++++ .../site-packages/sqlalchemy/engine/mock.py | 118 + .../sqlalchemy/engine/reflection.py | 1150 +++ .../site-packages/sqlalchemy/engine/result.py | 1787 ++++ .../site-packages/sqlalchemy/engine/row.py | 609 ++ .../sqlalchemy/engine/strategies.py | 17 + .../site-packages/sqlalchemy/engine/url.py | 796 ++ .../site-packages/sqlalchemy/engine/util.py | 234 + .../sqlalchemy/event/__init__.py | 17 + .../event/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 555 bytes .../event/__pycache__/api.cpython-38.pyc | Bin 0 -> 8249 bytes .../event/__pycache__/attr.cpython-38.pyc | Bin 0 -> 16260 bytes .../event/__pycache__/base.cpython-38.pyc | Bin 0 -> 10675 bytes .../event/__pycache__/legacy.cpython-38.pyc | Bin 0 -> 5126 bytes .../event/__pycache__/registry.cpython-38.pyc | Bin 0 -> 5933 bytes .../site-packages/sqlalchemy/event/api.py | 219 + .../site-packages/sqlalchemy/event/attr.py | 468 + .../site-packages/sqlalchemy/event/base.py | 345 + .../site-packages/sqlalchemy/event/legacy.py | 185 + .../sqlalchemy/event/registry.py | 297 + .../site-packages/sqlalchemy/events.py | 14 + .../python3.8/site-packages/sqlalchemy/exc.py | 733 ++ .../site-packages/sqlalchemy/ext/__init__.py | 11 + .../ext/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 301 bytes .../associationproxy.cpython-38.pyc | Bin 0 -> 50770 bytes .../ext/__pycache__/automap.cpython-38.pyc | Bin 0 -> 38013 bytes .../ext/__pycache__/baked.cpython-38.pyc | Bin 0 -> 19121 bytes .../ext/__pycache__/compiler.cpython-38.pyc | Bin 0 -> 16979 bytes .../horizontal_shard.cpython-38.pyc | Bin 0 -> 7099 bytes .../ext/__pycache__/hybrid.cpython-38.pyc | Bin 0 -> 42484 bytes .../ext/__pycache__/indexable.cpython-38.pyc | Bin 0 -> 10995 bytes .../instrumentation.cpython-38.pyc | Bin 0 -> 14317 bytes .../ext/__pycache__/mutable.cpython-38.pyc | Bin 0 -> 34258 bytes .../__pycache__/orderinglist.cpython-38.pyc | Bin 0 -> 14244 bytes .../ext/__pycache__/serializer.cpython-38.pyc | Bin 0 -> 5034 bytes .../sqlalchemy/ext/associationproxy.py | 1611 ++++ .../sqlalchemy/ext/asyncio/__init__.py | 21 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 812 bytes .../asyncio/__pycache__/base.cpython-38.pyc | Bin 0 -> 3224 bytes .../asyncio/__pycache__/engine.cpython-38.pyc | Bin 0 -> 24731 bytes .../asyncio/__pycache__/events.cpython-38.pyc | Bin 0 -> 1470 bytes .../asyncio/__pycache__/exc.cpython-38.pyc | Bin 0 -> 913 bytes .../asyncio/__pycache__/result.cpython-38.pyc | Bin 0 -> 21297 bytes .../__pycache__/scoping.cpython-38.pyc | Bin 0 -> 2930 bytes .../__pycache__/session.cpython-38.pyc | Bin 0 -> 18129 bytes .../sqlalchemy/ext/asyncio/base.py | 89 + .../sqlalchemy/ext/asyncio/engine.py | 813 ++ .../sqlalchemy/ext/asyncio/events.py | 36 + .../sqlalchemy/ext/asyncio/exc.py | 21 + .../sqlalchemy/ext/asyncio/result.py | 648 ++ .../sqlalchemy/ext/asyncio/scoping.py | 106 + .../sqlalchemy/ext/asyncio/session.py | 665 ++ .../site-packages/sqlalchemy/ext/automap.py | 1224 +++ .../site-packages/sqlalchemy/ext/baked.py | 648 ++ .../site-packages/sqlalchemy/ext/compiler.py | 508 + .../sqlalchemy/ext/declarative/__init__.py | 64 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1697 bytes .../__pycache__/extensions.cpython-38.pyc | Bin 0 -> 15338 bytes .../sqlalchemy/ext/declarative/extensions.py | 458 + .../sqlalchemy/ext/horizontal_shard.py | 256 + .../site-packages/sqlalchemy/ext/hybrid.py | 1200 +++ .../site-packages/sqlalchemy/ext/indexable.py | 352 + .../sqlalchemy/ext/instrumentation.py | 416 + .../site-packages/sqlalchemy/ext/mutable.py | 951 ++ .../sqlalchemy/ext/mypy/__init__.py | 0 .../mypy/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 211 bytes .../ext/mypy/__pycache__/apply.cpython-38.pyc | Bin 0 -> 5984 bytes .../__pycache__/decl_class.cpython-38.pyc | Bin 0 -> 8068 bytes .../ext/mypy/__pycache__/infer.cpython-38.pyc | Bin 0 -> 8935 bytes .../ext/mypy/__pycache__/names.cpython-38.pyc | Bin 0 -> 5454 bytes .../mypy/__pycache__/plugin.cpython-38.pyc | Bin 0 -> 7366 bytes .../ext/mypy/__pycache__/util.cpython-38.pyc | Bin 0 -> 8059 bytes .../sqlalchemy/ext/mypy/apply.py | 297 + .../sqlalchemy/ext/mypy/decl_class.py | 503 + .../sqlalchemy/ext/mypy/infer.py | 549 ++ .../sqlalchemy/ext/mypy/names.py | 247 + .../sqlalchemy/ext/mypy/plugin.py | 284 + .../site-packages/sqlalchemy/ext/mypy/util.py | 299 + .../sqlalchemy/ext/orderinglist.py | 388 + .../sqlalchemy/ext/serializer.py | 177 + .../sqlalchemy/future/__init__.py | 18 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 524 bytes .../future/__pycache__/engine.cpython-38.pyc | Bin 0 -> 16680 bytes .../site-packages/sqlalchemy/future/engine.py | 413 + .../sqlalchemy/future/orm/__init__.py | 10 + .../orm/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 259 bytes .../site-packages/sqlalchemy/inspection.py | 93 + .../python3.8/site-packages/sqlalchemy/log.py | 225 + .../site-packages/sqlalchemy/orm/__init__.py | 338 + .../orm/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 10609 bytes .../orm/__pycache__/attributes.cpython-38.pyc | Bin 0 -> 59654 bytes .../orm/__pycache__/base.cpython-38.pyc | Bin 0 -> 13061 bytes .../__pycache__/clsregistry.cpython-38.pyc | Bin 0 -> 12141 bytes .../__pycache__/collections.cpython-38.pyc | Bin 0 -> 54212 bytes .../orm/__pycache__/context.cpython-38.pyc | Bin 0 -> 51186 bytes .../orm/__pycache__/decl_api.cpython-38.pyc | Bin 0 -> 34555 bytes .../orm/__pycache__/decl_base.cpython-38.pyc | Bin 0 -> 25565 bytes .../orm/__pycache__/dependency.cpython-38.pyc | Bin 0 -> 23814 bytes .../descriptor_props.cpython-38.pyc | Bin 0 -> 25978 bytes .../orm/__pycache__/dynamic.cpython-38.pyc | Bin 0 -> 13387 bytes .../orm/__pycache__/evaluator.cpython-38.pyc | Bin 0 -> 7076 bytes .../orm/__pycache__/events.cpython-38.pyc | Bin 0 -> 111573 bytes .../orm/__pycache__/exc.cpython-38.pyc | Bin 0 -> 6865 bytes .../orm/__pycache__/identity.cpython-38.pyc | Bin 0 -> 7609 bytes .../instrumentation.cpython-38.pyc | Bin 0 -> 18628 bytes .../orm/__pycache__/interfaces.cpython-38.pyc | Bin 0 -> 29511 bytes .../orm/__pycache__/loading.cpython-38.pyc | Bin 0 -> 26195 bytes .../orm/__pycache__/mapper.cpython-38.pyc | Bin 0 -> 97381 bytes .../__pycache__/path_registry.cpython-38.pyc | Bin 0 -> 15012 bytes .../__pycache__/persistence.cpython-38.pyc | Bin 0 -> 41245 bytes .../orm/__pycache__/properties.cpython-38.pyc | Bin 0 -> 13855 bytes .../orm/__pycache__/query.cpython-38.pyc | Bin 0 -> 111961 bytes .../__pycache__/relationships.cpython-38.pyc | Bin 0 -> 107434 bytes .../orm/__pycache__/scoping.cpython-38.pyc | Bin 0 -> 6639 bytes .../orm/__pycache__/session.cpython-38.pyc | Bin 0 -> 130296 bytes .../orm/__pycache__/state.cpython-38.pyc | Bin 0 -> 28333 bytes .../orm/__pycache__/strategies.cpython-38.pyc | Bin 0 -> 57943 bytes .../strategy_options.cpython-38.pyc | Bin 0 -> 51459 bytes .../orm/__pycache__/sync.cpython-38.pyc | Bin 0 -> 3935 bytes .../orm/__pycache__/unitofwork.cpython-38.pyc | Bin 0 -> 21191 bytes .../orm/__pycache__/util.cpython-38.pyc | Bin 0 -> 62322 bytes .../sqlalchemy/orm/attributes.py | 2315 +++++ .../site-packages/sqlalchemy/orm/base.py | 572 ++ .../sqlalchemy/orm/clsregistry.py | 441 + .../sqlalchemy/orm/collections.py | 1706 ++++ .../site-packages/sqlalchemy/orm/context.py | 3003 ++++++ .../site-packages/sqlalchemy/orm/decl_api.py | 1045 +++ .../site-packages/sqlalchemy/orm/decl_base.py | 1167 +++ .../sqlalchemy/orm/dependency.py | 1290 +++ .../sqlalchemy/orm/descriptor_props.py | 745 ++ .../site-packages/sqlalchemy/orm/dynamic.py | 488 + .../site-packages/sqlalchemy/orm/evaluator.py | 241 + .../site-packages/sqlalchemy/orm/events.py | 2848 ++++++ .../site-packages/sqlalchemy/orm/exc.py | 204 + .../site-packages/sqlalchemy/orm/identity.py | 254 + .../sqlalchemy/orm/instrumentation.py | 650 ++ .../sqlalchemy/orm/interfaces.py | 940 ++ .../site-packages/sqlalchemy/orm/loading.py | 1465 +++ .../site-packages/sqlalchemy/orm/mapper.py | 3638 ++++++++ .../sqlalchemy/orm/path_registry.py | 519 ++ .../sqlalchemy/orm/persistence.py | 2407 +++++ .../sqlalchemy/orm/properties.py | 438 + .../site-packages/sqlalchemy/orm/query.py | 3485 +++++++ .../sqlalchemy/orm/relationships.py | 3689 ++++++++ .../site-packages/sqlalchemy/orm/scoping.py | 228 + .../site-packages/sqlalchemy/orm/session.py | 4338 +++++++++ .../site-packages/sqlalchemy/orm/state.py | 1023 ++ .../sqlalchemy/orm/strategies.py | 3135 +++++++ .../sqlalchemy/orm/strategy_options.py | 1998 ++++ .../site-packages/sqlalchemy/orm/sync.py | 167 + .../sqlalchemy/orm/unitofwork.py | 784 ++ .../site-packages/sqlalchemy/orm/util.py | 2074 +++++ .../site-packages/sqlalchemy/pool/__init__.py | 56 + .../pool/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 1379 bytes .../pool/__pycache__/base.cpython-38.pyc | Bin 0 -> 30878 bytes .../__pycache__/dbapi_proxy.cpython-38.pyc | Bin 0 -> 4865 bytes .../pool/__pycache__/events.cpython-38.pyc | Bin 0 -> 10716 bytes .../pool/__pycache__/impl.cpython-38.pyc | Bin 0 -> 15958 bytes .../site-packages/sqlalchemy/pool/base.py | 1121 +++ .../sqlalchemy/pool/dbapi_proxy.py | 147 + .../site-packages/sqlalchemy/pool/events.py | 278 + .../site-packages/sqlalchemy/pool/impl.py | 514 ++ .../site-packages/sqlalchemy/processors.py | 176 + .../site-packages/sqlalchemy/schema.py | 59 + .../site-packages/sqlalchemy/sql/__init__.py | 150 + .../sql/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 4218 bytes .../sql/__pycache__/annotation.cpython-38.pyc | Bin 0 -> 9613 bytes .../sql/__pycache__/base.cpython-38.pyc | Bin 0 -> 57243 bytes .../sql/__pycache__/coercions.cpython-38.pyc | Bin 0 -> 27318 bytes .../sql/__pycache__/compiler.cpython-38.pyc | Bin 0 -> 128323 bytes .../sql/__pycache__/crud.cpython-38.pyc | Bin 0 -> 17391 bytes .../sql/__pycache__/ddl.cpython-38.pyc | Bin 0 -> 40709 bytes .../default_comparator.cpython-38.pyc | Bin 0 -> 6853 bytes .../sql/__pycache__/dml.cpython-38.pyc | Bin 0 -> 46896 bytes .../sql/__pycache__/elements.cpython-38.pyc | Bin 0 -> 163861 bytes .../sql/__pycache__/events.cpython-38.pyc | Bin 0 -> 14121 bytes .../sql/__pycache__/expression.cpython-38.pyc | Bin 0 -> 7015 bytes .../sql/__pycache__/functions.cpython-38.pyc | Bin 0 -> 48794 bytes .../sql/__pycache__/lambdas.cpython-38.pyc | Bin 0 -> 34534 bytes .../sql/__pycache__/naming.cpython-38.pyc | Bin 0 -> 5275 bytes .../sql/__pycache__/operators.cpython-38.pyc | Bin 0 -> 50990 bytes .../sql/__pycache__/roles.cpython-38.pyc | Bin 0 -> 8893 bytes .../sql/__pycache__/schema.cpython-38.pyc | Bin 0 -> 165911 bytes .../sql/__pycache__/selectable.cpython-38.pyc | Bin 0 -> 214098 bytes .../sql/__pycache__/sqltypes.cpython-38.pyc | Bin 0 -> 104853 bytes .../sql/__pycache__/traversals.cpython-38.pyc | Bin 0 -> 44435 bytes .../sql/__pycache__/type_api.cpython-38.pyc | Bin 0 -> 58911 bytes .../sql/__pycache__/util.cpython-38.pyc | Bin 0 -> 26963 bytes .../sql/__pycache__/visitors.cpython-38.pyc | Bin 0 -> 22085 bytes .../sqlalchemy/sql/annotation.py | 358 + .../site-packages/sqlalchemy/sql/base.py | 1698 ++++ .../site-packages/sqlalchemy/sql/coercions.py | 1078 +++ .../site-packages/sqlalchemy/sql/compiler.py | 5446 +++++++++++ .../site-packages/sqlalchemy/sql/crud.py | 1080 +++ .../site-packages/sqlalchemy/sql/ddl.py | 1336 +++ .../sqlalchemy/sql/default_comparator.py | 356 + .../site-packages/sqlalchemy/sql/dml.py | 1432 +++ .../site-packages/sqlalchemy/sql/elements.py | 5362 +++++++++++ .../site-packages/sqlalchemy/sql/events.py | 331 + .../sqlalchemy/sql/expression.py | 278 + .../site-packages/sqlalchemy/sql/functions.py | 1547 ++++ .../site-packages/sqlalchemy/sql/lambdas.py | 1294 +++ .../site-packages/sqlalchemy/sql/naming.py | 210 + .../site-packages/sqlalchemy/sql/operators.py | 1667 ++++ .../site-packages/sqlalchemy/sql/roles.py | 234 + .../site-packages/sqlalchemy/sql/schema.py | 5136 +++++++++++ .../sqlalchemy/sql/selectable.py | 6755 ++++++++++++++ .../site-packages/sqlalchemy/sql/sqltypes.py | 3311 +++++++ .../sqlalchemy/sql/traversals.py | 1465 +++ .../site-packages/sqlalchemy/sql/type_api.py | 1801 ++++ .../site-packages/sqlalchemy/sql/util.py | 1120 +++ .../site-packages/sqlalchemy/sql/visitors.py | 852 ++ .../sqlalchemy/testing/__init__.py | 84 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 2820 bytes .../__pycache__/assertions.cpython-38.pyc | Bin 0 -> 23687 bytes .../__pycache__/assertsql.cpython-38.pyc | Bin 0 -> 12231 bytes .../__pycache__/asyncio.cpython-38.pyc | Bin 0 -> 3023 bytes .../testing/__pycache__/config.cpython-38.pyc | Bin 0 -> 6802 bytes .../__pycache__/engines.cpython-38.pyc | Bin 0 -> 13466 bytes .../__pycache__/entities.cpython-38.pyc | Bin 0 -> 2852 bytes .../__pycache__/exclusions.cpython-38.pyc | Bin 0 -> 14955 bytes .../__pycache__/fixtures.cpython-38.pyc | Bin 0 -> 21189 bytes .../testing/__pycache__/mock.cpython-38.pyc | Bin 0 -> 703 bytes .../__pycache__/pickleable.cpython-38.pyc | Bin 0 -> 5149 bytes .../__pycache__/profiling.cpython-38.pyc | Bin 0 -> 8540 bytes .../__pycache__/provision.cpython-38.pyc | Bin 0 -> 11212 bytes .../__pycache__/requirements.cpython-38.pyc | Bin 0 -> 58019 bytes .../testing/__pycache__/schema.cpython-38.pyc | Bin 0 -> 5986 bytes .../testing/__pycache__/util.cpython-38.pyc | Bin 0 -> 13045 bytes .../__pycache__/warnings.cpython-38.pyc | Bin 0 -> 1985 bytes .../sqlalchemy/testing/assertions.py | 785 ++ .../sqlalchemy/testing/assertsql.py | 457 + .../sqlalchemy/testing/asyncio.py | 129 + .../sqlalchemy/testing/config.py | 209 + .../sqlalchemy/testing/engines.py | 444 + .../sqlalchemy/testing/entities.py | 111 + .../sqlalchemy/testing/exclusions.py | 465 + .../sqlalchemy/testing/fixtures.py | 825 ++ .../site-packages/sqlalchemy/testing/mock.py | 32 + .../sqlalchemy/testing/pickleable.py | 141 + .../sqlalchemy/testing/plugin/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 217 bytes .../__pycache__/bootstrap.cpython-38.pyc | Bin 0 -> 1798 bytes .../__pycache__/plugin_base.cpython-38.pyc | Bin 0 -> 18257 bytes .../__pycache__/pytestplugin.cpython-38.pyc | Bin 0 -> 18806 bytes .../reinvent_fixtures_py2k.cpython-38.pyc | Bin 0 -> 3236 bytes .../sqlalchemy/testing/plugin/bootstrap.py | 50 + .../sqlalchemy/testing/plugin/plugin_base.py | 789 ++ .../sqlalchemy/testing/plugin/pytestplugin.py | 810 ++ .../testing/plugin/reinvent_fixtures_py2k.py | 112 + .../sqlalchemy/testing/profiling.py | 335 + .../sqlalchemy/testing/provision.py | 414 + .../sqlalchemy/testing/requirements.py | 1482 +++ .../sqlalchemy/testing/schema.py | 218 + .../sqlalchemy/testing/suite/__init__.py | 13 + .../suite/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 541 bytes .../suite/__pycache__/test_cte.cpython-38.pyc | Bin 0 -> 4679 bytes .../suite/__pycache__/test_ddl.cpython-38.pyc | Bin 0 -> 11476 bytes .../test_deprecations.cpython-38.pyc | Bin 0 -> 4329 bytes .../__pycache__/test_dialect.cpython-38.pyc | Bin 0 -> 10318 bytes .../__pycache__/test_insert.cpython-38.pyc | Bin 0 -> 8889 bytes .../test_reflection.cpython-38.pyc | Bin 0 -> 42660 bytes .../__pycache__/test_results.cpython-38.pyc | Bin 0 -> 11373 bytes .../__pycache__/test_rowcount.cpython-38.pyc | Bin 0 -> 4591 bytes .../__pycache__/test_select.cpython-38.pyc | Bin 0 -> 55155 bytes .../__pycache__/test_sequence.cpython-38.pyc | Bin 0 -> 8289 bytes .../__pycache__/test_types.cpython-38.pyc | Bin 0 -> 39562 bytes .../test_unicode_ddl.cpython-38.pyc | Bin 0 -> 4433 bytes .../test_update_delete.cpython-38.pyc | Bin 0 -> 2062 bytes .../sqlalchemy/testing/suite/test_cte.py | 204 + .../sqlalchemy/testing/suite/test_ddl.py | 381 + .../testing/suite/test_deprecations.py | 145 + .../sqlalchemy/testing/suite/test_dialect.py | 361 + .../sqlalchemy/testing/suite/test_insert.py | 367 + .../testing/suite/test_reflection.py | 1722 ++++ .../sqlalchemy/testing/suite/test_results.py | 426 + .../sqlalchemy/testing/suite/test_rowcount.py | 165 + .../sqlalchemy/testing/suite/test_select.py | 1783 ++++ .../sqlalchemy/testing/suite/test_sequence.py | 282 + .../sqlalchemy/testing/suite/test_types.py | 1440 +++ .../testing/suite/test_unicode_ddl.py | 206 + .../testing/suite/test_update_delete.py | 59 + .../site-packages/sqlalchemy/testing/util.py | 458 + .../sqlalchemy/testing/warnings.py | 84 + .../site-packages/sqlalchemy/types.py | 117 + .../site-packages/sqlalchemy/util/__init__.py | 174 + .../util/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 6166 bytes .../__pycache__/_collections.cpython-38.pyc | Bin 0 -> 37880 bytes .../__pycache__/_compat_py3k.cpython-38.pyc | Bin 0 -> 1956 bytes .../_concurrency_py3k.cpython-38.pyc | Bin 0 -> 4779 bytes .../__pycache__/_preloaded.cpython-38.pyc | Bin 0 -> 2673 bytes .../util/__pycache__/compat.cpython-38.pyc | Bin 0 -> 16354 bytes .../__pycache__/concurrency.cpython-38.pyc | Bin 0 -> 1778 bytes .../__pycache__/deprecations.cpython-38.pyc | Bin 0 -> 8998 bytes .../__pycache__/langhelpers.cpython-38.pyc | Bin 0 -> 50989 bytes .../util/__pycache__/queue.cpython-38.pyc | Bin 0 -> 8444 bytes .../__pycache__/topological.cpython-38.pyc | Bin 0 -> 2249 bytes .../sqlalchemy/util/_collections.py | 1089 +++ .../sqlalchemy/util/_compat_py3k.py | 67 + .../sqlalchemy/util/_concurrency_py3k.py | 202 + .../sqlalchemy/util/_preloaded.py | 68 + .../site-packages/sqlalchemy/util/compat.py | 631 ++ .../sqlalchemy/util/concurrency.py | 69 + .../sqlalchemy/util/deprecations.py | 416 + .../sqlalchemy/util/langhelpers.py | 1941 ++++ .../site-packages/sqlalchemy/util/queue.py | 291 + .../sqlalchemy/util/topological.py | 100 + .../tenacity-8.0.1.dist-info/AUTHORS | 46 + .../tenacity-8.0.1.dist-info/INSTALLER | 1 + .../tenacity-8.0.1.dist-info/LICENSE | 202 + .../tenacity-8.0.1.dist-info/METADATA | 30 + .../tenacity-8.0.1.dist-info/RECORD | 31 + .../tenacity-8.0.1.dist-info/WHEEL | 5 + .../tenacity-8.0.1.dist-info/pbr.json | 1 + .../tenacity-8.0.1.dist-info/top_level.txt | 1 + .../site-packages/tenacity/__init__.py | 515 ++ .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 16275 bytes .../__pycache__/_asyncio.cpython-38.pyc | Bin 0 -> 2578 bytes .../__pycache__/_utils.cpython-38.pyc | Bin 0 -> 1243 bytes .../tenacity/__pycache__/after.cpython-38.pyc | Bin 0 -> 1196 bytes .../__pycache__/before.cpython-38.pyc | Bin 0 -> 1088 bytes .../__pycache__/before_sleep.cpython-38.pyc | Bin 0 -> 1372 bytes .../tenacity/__pycache__/nap.cpython-38.pyc | Bin 0 -> 1198 bytes .../tenacity/__pycache__/retry.cpython-38.pyc | Bin 0 -> 8778 bytes .../tenacity/__pycache__/stop.cpython-38.pyc | Bin 0 -> 4246 bytes .../__pycache__/tornadoweb.cpython-38.pyc | Bin 0 -> 1732 bytes .../tenacity/__pycache__/wait.cpython-38.pyc | Bin 0 -> 7956 bytes .../site-packages/tenacity/_asyncio.py | 92 + .../site-packages/tenacity/_utils.py | 68 + .../python3.8/site-packages/tenacity/after.py | 46 + .../site-packages/tenacity/before.py | 41 + .../site-packages/tenacity/before_sleep.py | 58 + .../python3.8/site-packages/tenacity/nap.py | 43 + .../python3.8/site-packages/tenacity/py.typed | 0 .../python3.8/site-packages/tenacity/retry.py | 213 + .../python3.8/site-packages/tenacity/stop.py | 96 + .../site-packages/tenacity/tornadoweb.py | 59 + .../python3.8/site-packages/tenacity/wait.py | 191 + .../site-packages/werkzeug/__init__.py | 6 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 372 bytes .../__pycache__/_internal.cpython-38.pyc | Bin 0 -> 18556 bytes .../__pycache__/_reloader.cpython-38.pyc | Bin 0 -> 12029 bytes .../__pycache__/datastructures.cpython-38.pyc | Bin 0 -> 106535 bytes .../__pycache__/exceptions.cpython-38.pyc | Bin 0 -> 30229 bytes .../__pycache__/filesystem.cpython-38.pyc | Bin 0 -> 2105 bytes .../__pycache__/formparser.cpython-38.pyc | Bin 0 -> 14033 bytes .../werkzeug/__pycache__/http.cpython-38.pyc | Bin 0 -> 38226 bytes .../werkzeug/__pycache__/local.cpython-38.pyc | Bin 0 -> 22604 bytes .../__pycache__/routing.cpython-38.pyc | Bin 0 -> 73396 bytes .../__pycache__/security.cpython-38.pyc | Bin 0 -> 8215 bytes .../__pycache__/serving.cpython-38.pyc | Bin 0 -> 30790 bytes .../werkzeug/__pycache__/test.cpython-38.pyc | Bin 0 -> 39070 bytes .../__pycache__/testapp.cpython-38.pyc | Bin 0 -> 9642 bytes .../werkzeug/__pycache__/urls.cpython-38.pyc | Bin 0 -> 36730 bytes .../__pycache__/user_agent.cpython-38.pyc | Bin 0 -> 1853 bytes .../__pycache__/useragents.cpython-38.pyc | Bin 0 -> 6891 bytes .../werkzeug/__pycache__/utils.cpython-38.pyc | Bin 0 -> 33094 bytes .../werkzeug/__pycache__/wsgi.cpython-38.pyc | Bin 0 -> 30331 bytes .../site-packages/werkzeug/_internal.py | 626 ++ .../site-packages/werkzeug/_reloader.py | 430 + .../site-packages/werkzeug/datastructures.py | 3059 ++++++ .../site-packages/werkzeug/datastructures.pyi | 912 ++ .../site-packages/werkzeug/debug/__init__.py | 502 + .../debug/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 13029 bytes .../debug/__pycache__/console.cpython-38.pyc | Bin 0 -> 7941 bytes .../debug/__pycache__/repr.cpython-38.pyc | Bin 0 -> 8874 bytes .../debug/__pycache__/tbtools.cpython-38.pyc | Bin 0 -> 18088 bytes .../site-packages/werkzeug/debug/console.py | 211 + .../site-packages/werkzeug/debug/repr.py | 284 + .../werkzeug/debug/shared/FONT_LICENSE | 96 + .../werkzeug/debug/shared/ICON_LICENSE.md | 6 + .../werkzeug/debug/shared/console.png | Bin 0 -> 507 bytes .../werkzeug/debug/shared/debugger.js | 359 + .../werkzeug/debug/shared/less.png | Bin 0 -> 191 bytes .../werkzeug/debug/shared/more.png | Bin 0 -> 200 bytes .../werkzeug/debug/shared/source.png | Bin 0 -> 818 bytes .../werkzeug/debug/shared/style.css | 163 + .../werkzeug/debug/shared/ubuntu.ttf | Bin 0 -> 70220 bytes .../site-packages/werkzeug/debug/tbtools.py | 600 ++ .../site-packages/werkzeug/exceptions.py | 943 ++ .../site-packages/werkzeug/filesystem.py | 55 + .../site-packages/werkzeug/formparser.py | 495 + .../python3.8/site-packages/werkzeug/http.py | 1388 +++ .../python3.8/site-packages/werkzeug/local.py | 677 ++ .../werkzeug/middleware/__init__.py | 22 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 717 bytes .../__pycache__/dispatcher.cpython-38.pyc | Bin 0 -> 2778 bytes .../__pycache__/http_proxy.cpython-38.pyc | Bin 0 -> 6837 bytes .../__pycache__/lint.cpython-38.pyc | Bin 0 -> 12739 bytes .../__pycache__/profiler.cpython-38.pyc | Bin 0 -> 4975 bytes .../__pycache__/proxy_fix.cpython-38.pyc | Bin 0 -> 6204 bytes .../__pycache__/shared_data.cpython-38.pyc | Bin 0 -> 9883 bytes .../werkzeug/middleware/dispatcher.py | 78 + .../werkzeug/middleware/http_proxy.py | 230 + .../site-packages/werkzeug/middleware/lint.py | 420 + .../werkzeug/middleware/profiler.py | 139 + .../werkzeug/middleware/proxy_fix.py | 187 + .../werkzeug/middleware/shared_data.py | 320 + .../python3.8/site-packages/werkzeug/py.typed | 0 .../site-packages/werkzeug/routing.py | 2341 +++++ .../site-packages/werkzeug/sansio/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 205 bytes .../__pycache__/multipart.cpython-38.pyc | Bin 0 -> 6560 bytes .../sansio/__pycache__/request.cpython-38.pyc | Bin 0 -> 17215 bytes .../__pycache__/response.cpython-38.pyc | Bin 0 -> 22664 bytes .../sansio/__pycache__/utils.cpython-38.pyc | Bin 0 -> 3913 bytes .../werkzeug/sansio/multipart.py | 260 + .../site-packages/werkzeug/sansio/request.py | 548 ++ .../site-packages/werkzeug/sansio/response.py | 704 ++ .../site-packages/werkzeug/sansio/utils.py | 142 + .../site-packages/werkzeug/security.py | 247 + .../site-packages/werkzeug/serving.py | 1081 +++ .../python3.8/site-packages/werkzeug/test.py | 1326 +++ .../site-packages/werkzeug/testapp.py | 240 + .../python3.8/site-packages/werkzeug/urls.py | 1211 +++ .../site-packages/werkzeug/user_agent.py | 47 + .../site-packages/werkzeug/useragents.py | 215 + .../python3.8/site-packages/werkzeug/utils.py | 1099 +++ .../werkzeug/wrappers/__init__.py | 16 + .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 928 bytes .../__pycache__/accept.cpython-38.pyc | Bin 0 -> 830 bytes .../wrappers/__pycache__/auth.cpython-38.pyc | Bin 0 -> 1304 bytes .../__pycache__/base_request.cpython-38.pyc | Bin 0 -> 1783 bytes .../__pycache__/base_response.cpython-38.pyc | Bin 0 -> 1794 bytes .../common_descriptors.cpython-38.pyc | Bin 0 -> 1381 bytes .../wrappers/__pycache__/cors.cpython-38.pyc | Bin 0 -> 1289 bytes .../wrappers/__pycache__/etag.cpython-38.pyc | Bin 0 -> 1289 bytes .../wrappers/__pycache__/json.cpython-38.pyc | Bin 0 -> 822 bytes .../__pycache__/request.cpython-38.pyc | Bin 0 -> 21291 bytes .../__pycache__/response.cpython-38.pyc | Bin 0 -> 29735 bytes .../__pycache__/user_agent.cpython-38.pyc | Bin 0 -> 843 bytes .../site-packages/werkzeug/wrappers/accept.py | 14 + .../site-packages/werkzeug/wrappers/auth.py | 26 + .../werkzeug/wrappers/base_request.py | 36 + .../werkzeug/wrappers/base_response.py | 36 + .../werkzeug/wrappers/common_descriptors.py | 26 + .../site-packages/werkzeug/wrappers/cors.py | 26 + .../site-packages/werkzeug/wrappers/etag.py | 26 + .../site-packages/werkzeug/wrappers/json.py | 13 + .../werkzeug/wrappers/request.py | 660 ++ .../werkzeug/wrappers/response.py | 890 ++ .../werkzeug/wrappers/user_agent.py | 14 + .../python3.8/site-packages/werkzeug/wsgi.py | 982 ++ flask-app/venv/pyvenv.cfg | 3 + screenshots/APM Datadog Yaml port 5050.png | Bin 0 -> 12693 bytes screenshots/APM Datadog Yaml.png | Bin 0 -> 75889 bytes .../APM My to do app on the browser.png | Bin 0 -> 107901 bytes ...shboard on datadog website Get results.png | Bin 0 -> 225027 bytes screenshots/APM datadog dashboard 1.png | Bin 0 -> 166893 bytes screenshots/APM ddtrace python app run.png | Bin 0 -> 220165 bytes .../APM my to do app showing entries.png | Bin 0 -> 287107 bytes screenshots/Agent Reporting Metrics.png | Bin 0 -> 91095 bytes screenshots/Alert Email.png | Bin 0 -> 381923 bytes screenshots/Configure the monitor Message.png | Bin 0 -> 68487 bytes screenshots/Datadog Agent Yaml Tags 1.png | Bin 0 -> 29571 bytes screenshots/Datadog Dashboard Tags.png | Bin 0 -> 244564 bytes screenshots/Datadog Yaml Tags 2.png | Bin 0 -> 24967 bytes screenshots/Downtime Weekday Settings.png | Bin 0 -> 122160 bytes screenshots/Downtime Weekend settings.png | Bin 0 -> 174232 bytes .../Downtime monitor email on the weekend.png | Bin 0 -> 77071 bytes screenshots/Downtime setting dashboard.png | Bin 0 -> 207023 bytes .../Downtime settings weekday dashboard .png | Bin 0 -> 222231 bytes .../Downtime settings weekend dashboard.png | Bin 0 -> 263602 bytes .../Email notice of snapshot anamolies.png | Bin 0 -> 364062 bytes screenshots/Metric Monitor Json.png | Bin 0 -> 233970 bytes screenshots/Missing Data Email.png | Bin 0 -> 362078 bytes .../Monitor metric Dashboard Timeboard.png | Bin 0 -> 202075 bytes screenshots/My Hourly Timeboard.png | Bin 0 -> 159706 bytes screenshots/Postgres conf.d.png | Bin 0 -> 32138 bytes screenshots/Postman API Editor.png | Bin 0 -> 182678 bytes screenshots/Warn Email.png | Bin 0 -> 378452 bytes .../mymetric Dashboard 45 Interval.png | Bin 0 -> 206857 bytes screenshots/mymetric Dashboard.png | Bin 0 -> 106628 bytes screenshots/mymetric sum.png | Bin 0 -> 123934 bytes screenshots/mymetric yaml 45 interval.png | Bin 0 -> 19263 bytes screenshots/mymetric yaml.png | Bin 0 -> 34713 bytes screenshots/mymetric.py.png | Bin 0 -> 44248 bytes 2703 files changed, 477898 insertions(+) create mode 100644 flask-app/__pycache__/app.cpython-36.pyc create mode 100644 flask-app/app.py create mode 100644 flask-app/db.sqlite create mode 100644 flask-app/templates/base.html create mode 100644 flask-app/venv/bin/Activate.ps1 create mode 100644 flask-app/venv/bin/activate create mode 100644 flask-app/venv/bin/activate.csh create mode 100644 flask-app/venv/bin/activate.fish create mode 100755 flask-app/venv/bin/ddtrace-run create mode 100755 flask-app/venv/bin/easy_install create mode 100755 flask-app/venv/bin/easy_install-3.8 create mode 100755 flask-app/venv/bin/flask create mode 100755 flask-app/venv/bin/pip create mode 100755 flask-app/venv/bin/pip3 create mode 100755 flask-app/venv/bin/pip3.8 create mode 120000 flask-app/venv/bin/python create mode 120000 flask-app/venv/bin/python3 create mode 100644 flask-app/venv/include/site/python3.8/greenlet/greenlet.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/INSTALLER create mode 100644 flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/LICENSE.rst create mode 100644 flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/METADATA create mode 100644 flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/RECORD create mode 100644 flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/WHEEL create mode 100644 flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/entry_points.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/top_level.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/INSTALLER create mode 100644 flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/LICENSE.rst create mode 100644 flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/METADATA create mode 100644 flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/RECORD create mode 100644 flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/WHEEL create mode 100644 flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/top_level.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/INSTALLER create mode 100644 flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/LICENSE.rst create mode 100644 flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/METADATA create mode 100644 flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/RECORD create mode 100644 flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/WHEEL create mode 100644 flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/entry_points.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/top_level.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/INSTALLER create mode 100644 flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/LICENSE.rst create mode 100644 flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/METADATA create mode 100644 flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/RECORD create mode 100644 flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/WHEEL create mode 100644 flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/top_level.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/INSTALLER create mode 100644 flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/LICENSE create mode 100644 flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/METADATA create mode 100644 flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/RECORD create mode 100644 flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/WHEEL create mode 100644 flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/top_level.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/INSTALLER create mode 100644 flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/LICENSE.rst create mode 100644 flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/METADATA create mode 100644 flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/RECORD create mode 100644 flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/WHEEL create mode 100644 flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/top_level.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/__pycache__/ddtrace_gevent_check.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/__pycache__/easy_install.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/__pycache__/pyparsing.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/__pycache__/six.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/__init__.pyi create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/_cmp.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/_compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/_config.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/_funcs.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/_make.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/_next_gen.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/_version_info.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/converters.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/exceptions.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/filters.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/setters.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/validators.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/_cmp.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/_cmp.pyi create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/_compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/_config.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/_funcs.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/_make.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/_next_gen.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/_version_info.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/_version_info.pyi create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/converters.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/converters.pyi create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/exceptions.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/exceptions.pyi create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/filters.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/filters.pyi create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/py.typed create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/setters.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/setters.pyi create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/validators.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/attr/validators.pyi create mode 100644 flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/AUTHORS.rst create mode 100644 flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/INSTALLER create mode 100644 flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/LICENSE create mode 100644 flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/METADATA create mode 100644 flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/RECORD create mode 100644 flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/WHEEL create mode 100644 flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/top_level.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/benchmarks/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/benchmarks/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/benchmarks/bm/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/benchmarks/bm/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/benchmarks/bm/__pycache__/_scenario.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/benchmarks/bm/_scenario.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/INSTALLER create mode 100644 flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/LICENSE.rst create mode 100644 flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/METADATA create mode 100644 flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/RECORD create mode 100644 flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/WHEEL create mode 100644 flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/top_level.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/__pycache__/_compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/__pycache__/_termui_impl.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/__pycache__/_textwrap.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/__pycache__/_unicodefun.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/__pycache__/_winconsole.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/__pycache__/core.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/__pycache__/decorators.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/__pycache__/exceptions.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/__pycache__/formatting.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/__pycache__/globals.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/__pycache__/parser.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/__pycache__/shell_completion.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/__pycache__/termui.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/__pycache__/testing.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/__pycache__/types.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/__pycache__/utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/_compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/_termui_impl.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/_textwrap.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/_unicodefun.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/_winconsole.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/core.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/decorators.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/exceptions.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/formatting.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/globals.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/parser.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/py.typed create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/shell_completion.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/termui.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/testing.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/types.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/click/utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/INSTALLER create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/LICENSE create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/LICENSE.Apache create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/LICENSE.BSD3 create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/METADATA create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/NOTICE create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/RECORD create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/WHEEL create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/entry_points.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/top_level.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/_hooks.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/_version.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/constants.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/context.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/encoding.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/filters.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/helpers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/monkey.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/pin.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/provider.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/sampler.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/span.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/tracer.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/util.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/version.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/_hooks.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/_version.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/bootstrap/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/bootstrap/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/bootstrap/__pycache__/sitecustomize.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/bootstrap/sitecustomize.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/commands/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/commands/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/commands/__pycache__/ddtrace_run.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/commands/ddtrace_run.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/constants.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/context.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/__pycache__/trace_utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/__pycache__/util.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiobotocore/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiobotocore/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiobotocore/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiobotocore/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/__pycache__/middlewares.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/__pycache__/template.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/middlewares.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/template.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiopg/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiopg/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiopg/__pycache__/connection.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiopg/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiopg/connection.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiopg/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/algoliasearch/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/algoliasearch/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/algoliasearch/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/algoliasearch/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aredis/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aredis/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aredis/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aredis/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asgi/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asgi/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asgi/__pycache__/middleware.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asgi/__pycache__/utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asgi/middleware.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asgi/utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/__pycache__/compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/__pycache__/helpers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/__pycache__/provider.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/__pycache__/wrappers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/helpers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/provider.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/wrappers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/boto/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/boto/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/boto/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/boto/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/botocore/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/botocore/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/botocore/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/botocore/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/bottle/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/bottle/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/bottle/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/bottle/__pycache__/trace.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/bottle/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/bottle/trace.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cassandra/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cassandra/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cassandra/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cassandra/__pycache__/session.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cassandra/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cassandra/session.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__pycache__/app.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__pycache__/constants.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__pycache__/signals.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__pycache__/task.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__pycache__/utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/app.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/constants.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/signals.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/task.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cherrypy/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cherrypy/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cherrypy/__pycache__/middleware.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cherrypy/middleware.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/consul/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/consul/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/consul/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/consul/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dbapi/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dbapi/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/__pycache__/_asgi.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/__pycache__/compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/__pycache__/restframework.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/__pycache__/utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/_asgi.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/restframework.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/__pycache__/lock.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/__pycache__/region.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/lock.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/region.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/elasticsearch/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/elasticsearch/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/elasticsearch/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/elasticsearch/__pycache__/quantize.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/elasticsearch/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/elasticsearch/quantize.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/falcon/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/falcon/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/falcon/__pycache__/middleware.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/falcon/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/falcon/middleware.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/falcon/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/fastapi/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/fastapi/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/fastapi/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/fastapi/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/__pycache__/helpers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/__pycache__/middleware.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/__pycache__/wrappers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/helpers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/middleware.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/wrappers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask_cache/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask_cache/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask_cache/__pycache__/tracers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask_cache/__pycache__/utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask_cache/tracers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask_cache/utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/futures/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/futures/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/futures/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/futures/__pycache__/threading.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/futures/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/futures/threading.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/__pycache__/greenlet.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/__pycache__/provider.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/greenlet.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/provider.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/__pycache__/client_interceptor.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/__pycache__/constants.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/__pycache__/server_interceptor.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/__pycache__/utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/client_interceptor.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/constants.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/server_interceptor.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httplib/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httplib/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httplib/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httplib/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httpx/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httpx/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httpx/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httpx/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/jinja2/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/jinja2/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/jinja2/__pycache__/constants.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/jinja2/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/jinja2/constants.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/jinja2/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/kombu/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/kombu/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/kombu/__pycache__/constants.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/kombu/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/kombu/__pycache__/utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/kombu/constants.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/kombu/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/kombu/utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/logging/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/logging/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/logging/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/logging/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mako/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mako/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mako/__pycache__/constants.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mako/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mako/constants.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mako/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mariadb/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mariadb/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mariadb/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mariadb/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/molten/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/molten/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/molten/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/molten/__pycache__/wrappers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/molten/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/molten/wrappers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mongoengine/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mongoengine/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mongoengine/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mongoengine/__pycache__/trace.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mongoengine/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mongoengine/trace.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysql/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysql/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysql/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysql/__pycache__/tracers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysql/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysql/tracers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysqldb/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysqldb/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysqldb/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysqldb/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/psycopg/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/psycopg/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/psycopg/__pycache__/connection.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/psycopg/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/psycopg/connection.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/psycopg/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylibmc/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylibmc/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylibmc/__pycache__/addrs.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylibmc/__pycache__/client.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylibmc/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylibmc/addrs.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylibmc/client.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylibmc/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/__pycache__/compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/__pycache__/constants.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/__pycache__/middleware.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/__pycache__/renderer.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/constants.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/middleware.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/renderer.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymemcache/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymemcache/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymemcache/__pycache__/client.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymemcache/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymemcache/client.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymemcache/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/__pycache__/client.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/__pycache__/parse.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/client.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/parse.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymysql/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymysql/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymysql/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymysql/__pycache__/tracers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymysql/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymysql/tracers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pynamodb/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pynamodb/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pynamodb/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pynamodb/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyodbc/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyodbc/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyodbc/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyodbc/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/__pycache__/constants.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/__pycache__/trace.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/constants.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/trace.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pytest/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pytest/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pytest/__pycache__/constants.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pytest/__pycache__/plugin.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pytest/constants.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pytest/plugin.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/__pycache__/tracers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/__pycache__/util.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/tracers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/util.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/rediscluster/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/rediscluster/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/rediscluster/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/rediscluster/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/__pycache__/connection.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/__pycache__/constants.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/__pycache__/legacy.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/__pycache__/session.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/connection.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/constants.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/legacy.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/session.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/rq/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/rq/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sanic/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sanic/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sanic/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sanic/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/snowflake/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/snowflake/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/snowflake/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/snowflake/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlalchemy/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlalchemy/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlalchemy/__pycache__/engine.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlalchemy/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlalchemy/engine.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlalchemy/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlite3/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlite3/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlite3/__pycache__/connection.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlite3/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlite3/connection.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlite3/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/starlette/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/starlette/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/starlette/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/starlette/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/tornado/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/tornado/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/tornado/__pycache__/application.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/tornado/__pycache__/compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/tornado/__pycache__/constants.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/tornado/__pycache__/decorators.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/tornado/__pycache__/handlers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/tornado/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/tornado/__pycache__/stack_context.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/tornado/__pycache__/template.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/tornado/application.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/tornado/compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/tornado/constants.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/tornado/decorators.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/tornado/handlers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/tornado/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/tornado/stack_context.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/tornado/template.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/trace_utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/urllib3/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/urllib3/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/urllib3/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/urllib3/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/util.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/vertica/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/vertica/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/vertica/__pycache__/constants.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/vertica/__pycache__/patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/vertica/constants.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/vertica/patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/wsgi/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/wsgi/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/wsgi/__pycache__/wsgi.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/wsgi/wsgi.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/encoding.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/__pycache__/aws.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/__pycache__/cassandra.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/__pycache__/ci.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/__pycache__/consul.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/__pycache__/db.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/__pycache__/elasticsearch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/__pycache__/errors.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/__pycache__/git.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/__pycache__/http.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/__pycache__/kombu.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/__pycache__/memcached.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/__pycache__/mongo.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/__pycache__/net.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/__pycache__/priority.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/__pycache__/redis.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/__pycache__/sql.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/__pycache__/system.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/__pycache__/test.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/aws.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/cassandra.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/ci.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/consul.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/db.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/elasticsearch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/errors.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/git.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/http.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/kombu.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/memcached.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/mongo.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/net.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/priority.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/redis.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/sql.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/system.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/ext/test.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/filters.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/helpers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/http/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/http/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/http/__pycache__/headers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/http/headers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/__pycache__/agent.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/__pycache__/atexit.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/__pycache__/compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/__pycache__/debug.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/__pycache__/dogstatsd.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/__pycache__/encoding.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/__pycache__/forksafe.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/__pycache__/hostname.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/__pycache__/http.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/__pycache__/logger.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/__pycache__/nogevent.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/__pycache__/periodic.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/__pycache__/rate_limiter.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/__pycache__/service.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/__pycache__/sma.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/__pycache__/uds.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/__pycache__/uwsgi.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/__pycache__/writer.cpython-38.pyc create mode 100755 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/_encoding.cpython-38-darwin.so create mode 100755 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/_rand.cpython-38-darwin.so create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/agent.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/atexit.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/debug.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/dogstatsd.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/encoding.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/forksafe.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/hostname.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/http.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/logger.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/nogevent.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/periodic.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/processor/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/processor/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/processor/__pycache__/trace.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/processor/trace.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/rate_limiter.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/runtime/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/runtime/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/runtime/__pycache__/collector.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/runtime/__pycache__/constants.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/runtime/__pycache__/container.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/runtime/__pycache__/metric_collectors.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/runtime/__pycache__/runtime_metrics.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/runtime/__pycache__/tag_collectors.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/runtime/collector.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/runtime/constants.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/runtime/container.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/runtime/metric_collectors.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/runtime/runtime_metrics.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/runtime/tag_collectors.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/service.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/sma.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/uds.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/uwsgi.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/internal/writer.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/monkey.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/__pycache__/helpers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/__pycache__/settings.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/__pycache__/span.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/__pycache__/span_context.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/__pycache__/tags.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/__pycache__/tracer.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/__pycache__/utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/helpers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/propagation/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/propagation/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/propagation/__pycache__/binary.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/propagation/__pycache__/http.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/propagation/__pycache__/propagator.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/propagation/__pycache__/text.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/propagation/binary.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/propagation/http.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/propagation/propagator.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/propagation/text.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/settings.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/span.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/span_context.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/tags.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/tracer.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/opentracer/utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/pin.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/__pycache__/_traceback.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/__pycache__/auto.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/__pycache__/event.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/__pycache__/profiler.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/__pycache__/recorder.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/__pycache__/scheduler.cpython-38.pyc create mode 100755 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/_build.cpython-38-darwin.so create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/_traceback.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/auto.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/bootstrap/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/bootstrap/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/bootstrap/__pycache__/sitecustomize.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/bootstrap/sitecustomize.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/collector/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/collector/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/collector/__pycache__/memalloc.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/collector/__pycache__/threading.cpython-38.pyc create mode 100755 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/collector/_memalloc.cpython-38-darwin.so create mode 100755 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/collector/_task.cpython-38-darwin.so create mode 100755 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/collector/_threading.cpython-38-darwin.so create mode 100755 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/collector/_traceback.cpython-38-darwin.so create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/collector/memalloc.py create mode 100755 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/collector/stack.cpython-38-darwin.so create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/collector/threading.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/event.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/exporter/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/exporter/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/exporter/__pycache__/file.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/exporter/__pycache__/http.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/exporter/__pycache__/pprof_pb2.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/exporter/__pycache__/pprof_pre312_pb2.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/exporter/file.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/exporter/http.py create mode 100755 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/exporter/pprof.cpython-38-darwin.so create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/exporter/pprof_pb2.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/exporter/pprof_pre312_pb2.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/profiler.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/recorder.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/profiling/scheduler.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/propagation/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/propagation/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/propagation/__pycache__/http.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/propagation/__pycache__/utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/propagation/http.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/propagation/utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/provider.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/py.typed create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/runtime/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/runtime/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/sampler.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/settings/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/settings/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/settings/__pycache__/config.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/settings/__pycache__/exceptions.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/settings/__pycache__/http.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/settings/__pycache__/integration.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/settings/config.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/settings/exceptions.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/settings/http.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/settings/integration.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/span.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/tracer.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/util.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/__pycache__/attr.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/__pycache__/attrdict.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/__pycache__/cache.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/__pycache__/config.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/__pycache__/deprecation.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/__pycache__/formats.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/__pycache__/http.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/__pycache__/importlib.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/__pycache__/time.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/__pycache__/version.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/__pycache__/wrappers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/attr.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/attrdict.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/cache.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/config.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/deprecation.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/formats.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/http.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/importlib.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/time.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/version.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/utils/wrappers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/contextvars/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/contextvars/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/debtcollector/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/debtcollector/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/debtcollector/__pycache__/_utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/debtcollector/__pycache__/moves.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/debtcollector/__pycache__/removals.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/debtcollector/__pycache__/renames.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/debtcollector/__pycache__/updating.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/debtcollector/_utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/debtcollector/moves.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/debtcollector/removals.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/debtcollector/renames.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/debtcollector/updating.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/dogstatsd/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/dogstatsd/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/dogstatsd/__pycache__/base.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/dogstatsd/__pycache__/compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/dogstatsd/__pycache__/context.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/dogstatsd/__pycache__/context_async.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/dogstatsd/__pycache__/format.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/dogstatsd/__pycache__/route.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/dogstatsd/base.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/dogstatsd/compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/dogstatsd/context.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/dogstatsd/context_async.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/dogstatsd/format.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/dogstatsd/route.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/monotonic/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/monotonic/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/__pycache__/_common.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/__pycache__/_compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/__pycache__/_psaix.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/__pycache__/_psbsd.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/__pycache__/_pslinux.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/__pycache__/_psosx.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/__pycache__/_psposix.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/__pycache__/_pssunos.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/__pycache__/_pswindows.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/__pycache__/setup.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/_common.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/_compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/_psaix.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/_psbsd.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/_pslinux.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/_psosx.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/_psposix.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/_pssunos.py create mode 100755 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/_psutil_osx.cpython-38-darwin.so create mode 100755 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/_psutil_posix.cpython-38-darwin.so create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/_pswindows.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/psutil/setup.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/wrapt/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/wrapt/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/wrapt/__pycache__/decorators.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/wrapt/__pycache__/importer.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/wrapt/__pycache__/setup.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/wrapt/__pycache__/wrappers.cpython-38.pyc create mode 100755 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/wrapt/_wrappers.cpython-38-darwin.so create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/wrapt/decorators.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/wrapt/importer.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/wrapt/setup.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/vendor/wrapt/wrappers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace/version.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/ddtrace_gevent_check.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/easy_install.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/__main__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/__pycache__/__main__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/__pycache__/app.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/__pycache__/blueprints.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/__pycache__/cli.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/__pycache__/config.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/__pycache__/ctx.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/__pycache__/debughelpers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/__pycache__/globals.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/__pycache__/helpers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/__pycache__/logging.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/__pycache__/scaffold.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/__pycache__/sessions.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/__pycache__/signals.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/__pycache__/templating.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/__pycache__/testing.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/__pycache__/typing.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/__pycache__/views.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/__pycache__/wrappers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/app.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/blueprints.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/cli.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/config.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/ctx.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/debughelpers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/globals.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/helpers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/json/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/json/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/json/__pycache__/tag.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/json/tag.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/logging.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/py.typed create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/scaffold.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/sessions.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/signals.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/templating.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/testing.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/typing.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/views.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask/wrappers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask_sqlalchemy/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask_sqlalchemy/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask_sqlalchemy/__pycache__/_compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask_sqlalchemy/__pycache__/model.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask_sqlalchemy/__pycache__/utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask_sqlalchemy/_compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask_sqlalchemy/model.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/flask_sqlalchemy/utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/any_pb2.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/api_pb2.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/descriptor.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/descriptor_database.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/descriptor_pb2.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/descriptor_pool.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/duration_pb2.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/empty_pb2.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/field_mask_pb2.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/json_format.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/message.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/message_factory.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/proto_builder.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/reflection.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/service.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/service_reflection.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/source_context_pb2.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/struct_pb2.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/symbol_database.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/text_encoding.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/text_format.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/timestamp_pb2.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/type_pb2.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/__pycache__/wrappers_pb2.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/any_pb2.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/api_pb2.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/compiler/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/compiler/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/compiler/__pycache__/plugin_pb2.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/compiler/plugin_pb2.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/descriptor.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/descriptor_database.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/descriptor_pb2.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/descriptor_pool.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/duration_pb2.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/empty_pb2.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/field_mask_pb2.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/__pycache__/api_implementation.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/__pycache__/containers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/__pycache__/decoder.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/__pycache__/encoder.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/__pycache__/enum_type_wrapper.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/__pycache__/extension_dict.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/__pycache__/message_listener.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/__pycache__/python_message.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/__pycache__/type_checkers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/__pycache__/well_known_types.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/__pycache__/wire_format.cpython-38.pyc create mode 100755 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/_api_implementation.cpython-38-darwin.so create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/api_implementation.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/containers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/decoder.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/encoder.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/enum_type_wrapper.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/extension_dict.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/message_listener.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/python_message.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/type_checkers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/well_known_types.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/internal/wire_format.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/json_format.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/message.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/message_factory.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/proto_builder.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/pyext/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/pyext/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/pyext/__pycache__/cpp_message.cpython-38.pyc create mode 100755 flask-app/venv/lib/python3.8/site-packages/google/protobuf/pyext/_message.cpython-38-darwin.so create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/pyext/cpp_message.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/reflection.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/service.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/service_reflection.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/source_context_pb2.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/struct_pb2.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/symbol_database.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/text_encoding.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/text_format.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/timestamp_pb2.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/type_pb2.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/util/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/util/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/util/__pycache__/json_format_pb2.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/util/__pycache__/json_format_proto3_pb2.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/util/json_format_pb2.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/util/json_format_proto3_pb2.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/google/protobuf/wrappers_pb2.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet-1.1.2.dist-info/AUTHORS create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet-1.1.2.dist-info/INSTALLER create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet-1.1.2.dist-info/LICENSE create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet-1.1.2.dist-info/LICENSE.PSF create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet-1.1.2.dist-info/METADATA create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet-1.1.2.dist-info/RECORD create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet-1.1.2.dist-info/WHEEL create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet-1.1.2.dist-info/top_level.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/__pycache__/__init__.cpython-38.pyc create mode 100755 flask-app/venv/lib/python3.8/site-packages/greenlet/_greenlet.cpython-38-darwin.so create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/greenlet.c create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/greenlet.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/setup_switch_x64_masm.cmd create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_aarch64_gcc.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_alpha_unix.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_amd64_unix.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_arm32_gcc.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_arm32_ios.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_csky_gcc.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_m68k_gcc.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_mips_unix.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_ppc64_aix.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_ppc64_linux.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_ppc_aix.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_ppc_linux.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_ppc_macosx.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_ppc_unix.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_riscv_unix.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_s390_unix.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_sparc_sun_gcc.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_x32_unix.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_x64_masm.asm create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_x64_masm.obj create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_x64_msvc.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_x86_msvc.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/platform/switch_x86_unix.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/slp_platformselect.h create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/__pycache__/test_contextvars.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/__pycache__/test_cpp.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/__pycache__/test_extension_interface.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/__pycache__/test_gc.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/__pycache__/test_generator.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/__pycache__/test_generator_nested.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/__pycache__/test_greenlet.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/__pycache__/test_leaks.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/__pycache__/test_stack_saved.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/__pycache__/test_throw.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/__pycache__/test_tracing.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/__pycache__/test_version.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/__pycache__/test_weakref.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/_test_extension.c create mode 100755 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/_test_extension.cpython-38-darwin.so create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/_test_extension_cpp.cpp create mode 100755 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/_test_extension_cpp.cpython-38-darwin.so create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/test_contextvars.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/test_cpp.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/test_extension_interface.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/test_gc.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/test_generator.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/test_generator_nested.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/test_greenlet.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/test_leaks.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/test_stack_saved.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/test_throw.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/test_tracing.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/test_version.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/greenlet/tests/test_weakref.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous-2.0.1.dist-info/INSTALLER create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous-2.0.1.dist-info/LICENSE.rst create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous-2.0.1.dist-info/METADATA create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous-2.0.1.dist-info/RECORD create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous-2.0.1.dist-info/WHEEL create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous-2.0.1.dist-info/top_level.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous/__pycache__/_json.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous/__pycache__/encoding.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous/__pycache__/exc.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous/__pycache__/jws.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous/__pycache__/serializer.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous/__pycache__/signer.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous/__pycache__/timed.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous/__pycache__/url_safe.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous/_json.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous/encoding.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous/exc.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous/jws.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous/py.typed create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous/serializer.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous/signer.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous/timed.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/itsdangerous/url_safe.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/_identifier.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/async_utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/bccache.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/compiler.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/constants.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/debug.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/defaults.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/environment.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/exceptions.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/ext.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/filters.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/idtracking.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/lexer.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/loaders.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/meta.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/nativetypes.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/nodes.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/optimizer.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/parser.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/runtime.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/sandbox.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/tests.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/__pycache__/visitor.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/_identifier.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/async_utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/bccache.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/compiler.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/constants.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/debug.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/defaults.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/environment.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/exceptions.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/ext.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/filters.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/idtracking.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/lexer.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/loaders.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/meta.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/nativetypes.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/nodes.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/optimizer.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/parser.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/py.typed create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/runtime.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/sandbox.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/tests.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/jinja2/visitor.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/markupsafe/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/markupsafe/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/markupsafe/__pycache__/_native.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/markupsafe/_native.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/markupsafe/_speedups.c create mode 100755 flask-app/venv/lib/python3.8/site-packages/markupsafe/_speedups.cpython-38-darwin.so create mode 100644 flask-app/venv/lib/python3.8/site-packages/markupsafe/_speedups.pyi create mode 100644 flask-app/venv/lib/python3.8/site-packages/markupsafe/py.typed create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging-21.2.dist-info/INSTALLER create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging-21.2.dist-info/LICENSE create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging-21.2.dist-info/LICENSE.APACHE create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging-21.2.dist-info/LICENSE.BSD create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging-21.2.dist-info/METADATA create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging-21.2.dist-info/RECORD create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging-21.2.dist-info/WHEEL create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging-21.2.dist-info/top_level.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/__about__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/__pycache__/__about__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/__pycache__/_manylinux.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/__pycache__/_musllinux.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/__pycache__/_structures.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/__pycache__/markers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/__pycache__/requirements.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/__pycache__/specifiers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/__pycache__/tags.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/__pycache__/utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/__pycache__/version.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/_manylinux.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/_musllinux.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/_structures.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/markers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/py.typed create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/requirements.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/specifiers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/tags.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/packaging/version.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip-19.2.3.dist-info/INSTALLER create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip-19.2.3.dist-info/LICENSE.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip-19.2.3.dist-info/METADATA create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip-19.2.3.dist-info/RECORD create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip-19.2.3.dist-info/WHEEL create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip-19.2.3.dist-info/entry_points.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip-19.2.3.dist-info/top_level.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/__main__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/__pycache__/__main__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/build_env.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/cache.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/configuration.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/download.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/exceptions.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/index.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/legacy_resolve.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/locations.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/pep425tags.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/pyproject.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/__pycache__/wheel.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/build_env.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/cache.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/cli/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/autocompletion.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/base_command.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/cmdoptions.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/main_parser.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/parser.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/cli/__pycache__/status_codes.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/cli/autocompletion.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/cli/base_command.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/cli/cmdoptions.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/cli/main_parser.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/cli/parser.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/cli/status_codes.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/check.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/completion.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/configuration.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/debug.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/download.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/freeze.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/hash.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/help.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/install.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/list.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/search.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/show.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/uninstall.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/__pycache__/wheel.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/check.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/completion.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/configuration.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/debug.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/download.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/freeze.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/hash.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/help.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/install.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/list.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/search.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/show.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/uninstall.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/commands/wheel.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/configuration.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/distributions/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/distributions/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/distributions/__pycache__/base.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/distributions/__pycache__/installed.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/distributions/__pycache__/source.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/distributions/__pycache__/wheel.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/distributions/base.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/distributions/installed.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/distributions/source.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/distributions/wheel.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/download.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/exceptions.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/index.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/legacy_resolve.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/locations.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/models/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/candidate.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/format_control.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/index.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/link.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/search_scope.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/selection_prefs.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/models/__pycache__/target_python.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/models/candidate.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/models/format_control.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/models/index.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/models/link.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/models/search_scope.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/models/selection_prefs.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/models/target_python.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/operations/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/operations/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/operations/__pycache__/check.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/operations/__pycache__/freeze.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/operations/__pycache__/prepare.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/operations/check.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/operations/freeze.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/operations/prepare.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/pep425tags.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/pyproject.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/req/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/constructors.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/req_file.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/req_install.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/req_set.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/req_tracker.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/req/__pycache__/req_uninstall.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/req/constructors.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/req/req_file.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/req/req_install.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/req/req_set.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/req/req_tracker.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/req/req_uninstall.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/appdirs.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/deprecation.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/encoding.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/filesystem.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/glibc.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/hashes.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/logging.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/marker_files.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/misc.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/models.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/outdated.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/packaging.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/setuptools_build.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/temp_dir.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/typing.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/ui.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/__pycache__/virtualenv.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/appdirs.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/deprecation.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/encoding.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/filesystem.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/glibc.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/hashes.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/logging.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/marker_files.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/misc.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/models.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/outdated.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/packaging.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/setuptools_build.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/temp_dir.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/typing.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/ui.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/utils/virtualenv.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/vcs/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/bazaar.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/git.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/mercurial.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/subversion.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/vcs/__pycache__/versioncontrol.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/vcs/bazaar.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/vcs/git.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/vcs/mercurial.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/vcs/subversion.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/vcs/versioncontrol.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_internal/wheel.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/__pycache__/appdirs.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/__pycache__/distro.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/__pycache__/ipaddress.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/__pycache__/pyparsing.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/__pycache__/retrying.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/__pycache__/six.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/appdirs.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/__pycache__/adapter.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/__pycache__/cache.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/__pycache__/compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/__pycache__/controller.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/__pycache__/serialize.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/_cmd.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/adapter.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/cache.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/caches/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/caches/file_cache.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/caches/redis_cache.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/controller.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/filewrapper.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/heuristics.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/serialize.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/cachecontrol/wrapper.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/certifi/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/certifi/__main__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/certifi/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/certifi/__pycache__/__main__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/certifi/__pycache__/core.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/certifi/cacert.pem create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/certifi/core.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/big5freq.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/big5prober.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/chardistribution.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/charsetgroupprober.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/charsetprober.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/codingstatemachine.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/cp949prober.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/enums.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/escprober.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/escsm.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/eucjpprober.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/euckrfreq.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/euckrprober.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/euctwfreq.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/euctwprober.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/gb2312freq.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/gb2312prober.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/hebrewprober.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/jisfreq.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/jpcntx.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langbulgarianmodel.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langcyrillicmodel.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langgreekmodel.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langhebrewmodel.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langhungarianmodel.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langthaimodel.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/langturkishmodel.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/latin1prober.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/mbcharsetprober.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/mbcsgroupprober.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/mbcssm.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/sbcharsetprober.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/sbcsgroupprober.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/sjisprober.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/universaldetector.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/utf8prober.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/__pycache__/version.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/big5freq.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/big5prober.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/chardistribution.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/charsetgroupprober.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/charsetprober.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/cli/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/cli/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/cli/__pycache__/chardetect.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/cli/chardetect.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/codingstatemachine.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/cp949prober.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/enums.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/escprober.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/escsm.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/eucjpprober.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/euckrfreq.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/euckrprober.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/euctwfreq.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/euctwprober.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/gb2312freq.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/gb2312prober.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/hebrewprober.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/jisfreq.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/jpcntx.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/langbulgarianmodel.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/langcyrillicmodel.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/langgreekmodel.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/langhebrewmodel.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/langhungarianmodel.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/langthaimodel.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/langturkishmodel.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/latin1prober.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/mbcharsetprober.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/mbcsgroupprober.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/mbcssm.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/sbcharsetprober.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/sbcsgroupprober.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/sjisprober.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/universaldetector.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/utf8prober.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/chardet/version.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/colorama/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/colorama/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/colorama/__pycache__/ansi.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/colorama/__pycache__/ansitowin32.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/colorama/__pycache__/initialise.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/colorama/__pycache__/win32.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/colorama/__pycache__/winterm.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/colorama/ansi.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/colorama/ansitowin32.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/colorama/initialise.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/colorama/win32.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/colorama/winterm.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/database.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/index.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/locators.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/manifest.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/markers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/metadata.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/resources.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/scripts.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/util.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/version.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/__pycache__/wheel.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/__pycache__/misc.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/__pycache__/shutil.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/__pycache__/sysconfig.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/__pycache__/tarfile.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/misc.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/shutil.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/sysconfig.cfg create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/sysconfig.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/_backport/tarfile.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/database.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/index.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/locators.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/manifest.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/markers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/metadata.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/resources.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/scripts.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/t32.exe create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/t64.exe create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/util.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/version.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/w32.exe create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/w64.exe create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distlib/wheel.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/distro.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/__pycache__/_ihatexml.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/__pycache__/_inputstream.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/__pycache__/_tokenizer.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/__pycache__/_utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/__pycache__/constants.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/__pycache__/html5parser.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/__pycache__/serializer.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_ihatexml.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_inputstream.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_tokenizer.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_trie/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_trie/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_trie/__pycache__/_base.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_trie/__pycache__/datrie.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_trie/__pycache__/py.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_trie/_base.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_trie/datrie.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_trie/py.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/_utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/constants.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/filters/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/filters/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/filters/__pycache__/alphabeticalattributes.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/filters/__pycache__/base.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/filters/__pycache__/inject_meta_charset.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/filters/__pycache__/lint.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/filters/__pycache__/optionaltags.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/filters/__pycache__/sanitizer.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/filters/__pycache__/whitespace.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/filters/alphabeticalattributes.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/filters/base.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/filters/inject_meta_charset.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/filters/lint.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/filters/optionaltags.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/filters/sanitizer.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/filters/whitespace.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/html5parser.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/serializer.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treeadapters/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treeadapters/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treeadapters/__pycache__/genshi.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treeadapters/__pycache__/sax.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treeadapters/genshi.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treeadapters/sax.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treebuilders/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/base.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/dom.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/etree.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treebuilders/__pycache__/etree_lxml.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treebuilders/base.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treebuilders/dom.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treebuilders/etree.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treebuilders/etree_lxml.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/base.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/dom.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/etree.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/etree_lxml.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/__pycache__/genshi.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/base.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/dom.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/etree.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/etree_lxml.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/html5lib/treewalkers/genshi.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/idna/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/idna/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/idna/__pycache__/codec.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/idna/__pycache__/compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/idna/__pycache__/core.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/idna/__pycache__/idnadata.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/idna/__pycache__/intranges.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/idna/__pycache__/package_data.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/idna/__pycache__/uts46data.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/idna/codec.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/idna/compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/idna/core.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/idna/idnadata.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/idna/intranges.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/idna/package_data.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/idna/uts46data.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/ipaddress.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/lockfile/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/lockfile/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/lockfile/__pycache__/linklockfile.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/lockfile/__pycache__/mkdirlockfile.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/lockfile/__pycache__/pidlockfile.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/lockfile/__pycache__/sqlitelockfile.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/lockfile/__pycache__/symlinklockfile.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/lockfile/linklockfile.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/lockfile/mkdirlockfile.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/lockfile/pidlockfile.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/lockfile/sqlitelockfile.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/lockfile/symlinklockfile.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/__pycache__/_version.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/__pycache__/exceptions.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/__pycache__/fallback.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/_version.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/exceptions.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/msgpack/fallback.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__about__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/__about__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/_compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/_structures.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/markers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/requirements.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/specifiers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/packaging/__pycache__/version.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/packaging/_compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/packaging/_structures.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/packaging/markers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/packaging/requirements.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/packaging/specifiers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/packaging/utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/packaging/version.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/_in_process.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/build.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/check.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/colorlog.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/envbuild.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pep517/__pycache__/wrappers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pep517/_in_process.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pep517/build.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pep517/check.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pep517/colorlog.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pep517/compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pep517/envbuild.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pep517/wrappers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pkg_resources/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pkg_resources/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pkg_resources/__pycache__/py31compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pkg_resources/py31compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/progress/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/progress/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/progress/__pycache__/bar.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/progress/__pycache__/counter.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/progress/__pycache__/spinner.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/progress/bar.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/progress/counter.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/progress/spinner.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pyparsing.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pytoml/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pytoml/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pytoml/__pycache__/core.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pytoml/__pycache__/parser.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pytoml/__pycache__/test.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pytoml/__pycache__/utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pytoml/__pycache__/writer.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pytoml/core.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pytoml/parser.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pytoml/test.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pytoml/utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/pytoml/writer.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/__pycache__/__version__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/__pycache__/_internal_utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/__pycache__/adapters.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/__pycache__/api.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/__pycache__/auth.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/__pycache__/certs.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/__pycache__/compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/__pycache__/cookies.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/__pycache__/exceptions.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/__pycache__/help.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/__pycache__/hooks.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/__pycache__/models.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/__pycache__/packages.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/__pycache__/sessions.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/__pycache__/status_codes.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/__pycache__/structures.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/__pycache__/utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/__version__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/_internal_utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/adapters.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/api.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/auth.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/certs.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/cookies.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/exceptions.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/help.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/hooks.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/models.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/packages.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/sessions.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/status_codes.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/structures.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/requests/utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/retrying.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/six.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/__pycache__/_collections.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/__pycache__/connection.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/__pycache__/connectionpool.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/__pycache__/exceptions.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/__pycache__/fields.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/__pycache__/filepost.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/__pycache__/poolmanager.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/__pycache__/request.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/__pycache__/response.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/_collections.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/connection.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/connectionpool.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/__pycache__/appengine.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/__pycache__/ntlmpool.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/__pycache__/pyopenssl.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/__pycache__/securetransport.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/_appengine_environ.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/_securetransport/__pycache__/low_level.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/_securetransport/bindings.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/_securetransport/low_level.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/appengine.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/ntlmpool.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/pyopenssl.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/securetransport.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/contrib/socks.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/exceptions.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/fields.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/filepost.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/__pycache__/six.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/backports/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/backports/__pycache__/makefile.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/backports/makefile.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/__pycache__/_mixin.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/__pycache__/abnf_regexp.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/__pycache__/api.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/__pycache__/builder.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/__pycache__/compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/__pycache__/exceptions.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/__pycache__/iri.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/__pycache__/misc.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/__pycache__/normalizers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/__pycache__/parseresult.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/__pycache__/uri.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/__pycache__/validators.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/_mixin.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/abnf_regexp.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/api.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/builder.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/exceptions.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/iri.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/misc.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/normalizers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/parseresult.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/uri.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/rfc3986/validators.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/six.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/__pycache__/_implementation.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/packages/ssl_match_hostname/_implementation.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/poolmanager.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/request.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/response.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/__pycache__/connection.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/__pycache__/queue.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/__pycache__/request.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/__pycache__/response.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/__pycache__/retry.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/__pycache__/timeout.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/__pycache__/url.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/__pycache__/wait.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/connection.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/queue.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/request.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/response.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/retry.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/ssl_.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/timeout.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/url.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/urllib3/util/wait.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/webencodings/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/webencodings/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/webencodings/__pycache__/labels.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/webencodings/__pycache__/mklabels.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/webencodings/__pycache__/tests.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/webencodings/__pycache__/x_user_defined.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/webencodings/labels.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/webencodings/mklabels.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/webencodings/tests.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pip/_vendor/webencodings/x_user_defined.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/__pycache__/py31compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/__pycache__/appdirs.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/__pycache__/pyparsing.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/__pycache__/six.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/appdirs.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__about__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/__about__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/_compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/_structures.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/markers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/requirements.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/specifiers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/__pycache__/version.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/_compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/_structures.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/markers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/requirements.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/specifiers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/packaging/version.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/pyparsing.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/_vendor/six.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/extern/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/extern/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/pkg_resources/py31compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/protobuf-3.19.1-py3.8-nspkg.pth create mode 100644 flask-app/venv/lib/python3.8/site-packages/protobuf-3.19.1.dist-info/INSTALLER create mode 100644 flask-app/venv/lib/python3.8/site-packages/protobuf-3.19.1.dist-info/METADATA create mode 100644 flask-app/venv/lib/python3.8/site-packages/protobuf-3.19.1.dist-info/RECORD create mode 100644 flask-app/venv/lib/python3.8/site-packages/protobuf-3.19.1.dist-info/WHEEL create mode 100644 flask-app/venv/lib/python3.8/site-packages/protobuf-3.19.1.dist-info/namespace_packages.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/protobuf-3.19.1.dist-info/top_level.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/pyparsing-2.4.7.dist-info/INSTALLER create mode 100644 flask-app/venv/lib/python3.8/site-packages/pyparsing-2.4.7.dist-info/LICENSE create mode 100644 flask-app/venv/lib/python3.8/site-packages/pyparsing-2.4.7.dist-info/METADATA create mode 100644 flask-app/venv/lib/python3.8/site-packages/pyparsing-2.4.7.dist-info/RECORD create mode 100644 flask-app/venv/lib/python3.8/site-packages/pyparsing-2.4.7.dist-info/WHEEL create mode 100644 flask-app/venv/lib/python3.8/site-packages/pyparsing-2.4.7.dist-info/top_level.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/pyparsing.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools-41.2.0.dist-info/INSTALLER create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools-41.2.0.dist-info/LICENSE create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools-41.2.0.dist-info/METADATA create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools-41.2.0.dist-info/RECORD create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools-41.2.0.dist-info/WHEEL create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools-41.2.0.dist-info/dependency_links.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools-41.2.0.dist-info/entry_points.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools-41.2.0.dist-info/top_level.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools-41.2.0.dist-info/zip-safe create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/_deprecation_warning.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/archive_util.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/build_meta.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/config.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/dep_util.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/depends.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/dist.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/extension.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/glibc.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/glob.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/launch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/lib2to3_ex.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/monkey.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/msvc.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/namespaces.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/package_index.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/pep425tags.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/py27compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/py31compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/py33compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/sandbox.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/site-patch.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/ssl_support.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/unicode_utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/version.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/wheel.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/__pycache__/windows_support.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_deprecation_warning.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/__pycache__/pyparsing.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/__pycache__/six.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__about__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/__about__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/_compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/_structures.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/markers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/requirements.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/specifiers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/__pycache__/version.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/_compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/_structures.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/markers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/requirements.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/specifiers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/packaging/version.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/pyparsing.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/_vendor/six.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/archive_util.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/build_meta.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/cli-32.exe create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/cli-64.exe create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/cli.exe create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/alias.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/bdist_egg.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/bdist_rpm.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/bdist_wininst.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/build_clib.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/build_ext.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/build_py.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/develop.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/dist_info.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/easy_install.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/egg_info.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install_egg_info.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install_lib.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/install_scripts.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/py36compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/register.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/rotate.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/saveopts.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/sdist.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/setopt.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/test.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/upload.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/__pycache__/upload_docs.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/alias.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/bdist_egg.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/bdist_rpm.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/bdist_wininst.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/build_clib.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/build_ext.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/build_py.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/develop.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/dist_info.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/easy_install.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/egg_info.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/install.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/install_egg_info.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/install_lib.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/install_scripts.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/launcher manifest.xml create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/py36compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/register.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/rotate.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/saveopts.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/sdist.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/setopt.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/test.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/upload.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/command/upload_docs.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/config.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/dep_util.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/depends.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/dist.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/extension.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/extern/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/extern/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/glibc.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/glob.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/gui-32.exe create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/gui-64.exe create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/gui.exe create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/launch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/lib2to3_ex.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/monkey.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/msvc.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/namespaces.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/package_index.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/pep425tags.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/py27compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/py31compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/py33compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/sandbox.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/script (dev).tmpl create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/script.tmpl create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/site-patch.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/ssl_support.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/unicode_utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/version.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/wheel.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/setuptools/windows_support.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/INSTALLER create mode 100644 flask-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/LICENSE create mode 100644 flask-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/METADATA create mode 100644 flask-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/RECORD create mode 100644 flask-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/WHEEL create mode 100644 flask-app/venv/lib/python3.8/site-packages/six-1.16.0.dist-info/top_level.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/six.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/__pycache__/events.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/__pycache__/exc.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/__pycache__/inspection.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/__pycache__/log.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/__pycache__/processors.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/__pycache__/schema.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/__pycache__/types.cpython-38.pyc create mode 100755 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/cimmutabledict.cpython-38-darwin.so create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/connectors/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/connectors/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/connectors/__pycache__/mxodbc.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/connectors/__pycache__/pyodbc.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/connectors/mxodbc.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/connectors/pyodbc.py create mode 100755 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/cprocessors.cpython-38-darwin.so create mode 100755 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/cresultproxy.cpython-38-darwin.so create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/databases/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/databases/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/firebird/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/firebird/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/firebird/__pycache__/base.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/firebird/__pycache__/fdb.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/firebird/__pycache__/kinterbasdb.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/firebird/base.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/firebird/fdb.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/firebird/kinterbasdb.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mssql/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mssql/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mssql/__pycache__/base.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mssql/__pycache__/information_schema.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mssql/__pycache__/json.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mssql/__pycache__/mxodbc.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mssql/__pycache__/provision.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mssql/__pycache__/pymssql.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mssql/__pycache__/pyodbc.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mssql/base.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mssql/information_schema.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mssql/json.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mssql/mxodbc.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mssql/provision.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mssql/pymssql.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mssql/pyodbc.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/__pycache__/aiomysql.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/__pycache__/asyncmy.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/__pycache__/base.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/__pycache__/cymysql.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/__pycache__/dml.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/__pycache__/enumerated.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/__pycache__/expression.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/__pycache__/json.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/__pycache__/mariadb.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/__pycache__/mariadbconnector.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/__pycache__/mysqlconnector.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/__pycache__/mysqldb.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/__pycache__/oursql.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/__pycache__/provision.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/__pycache__/pymysql.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/__pycache__/pyodbc.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/__pycache__/reflection.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/__pycache__/reserved_words.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/__pycache__/types.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/aiomysql.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/asyncmy.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/base.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/cymysql.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/dml.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/enumerated.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/expression.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/json.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/mariadb.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/mariadbconnector.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/mysqlconnector.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/mysqldb.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/oursql.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/provision.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/pymysql.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/pyodbc.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/reflection.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/reserved_words.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/mysql/types.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/oracle/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/oracle/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/oracle/__pycache__/base.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/oracle/__pycache__/cx_oracle.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/oracle/__pycache__/provision.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/oracle/base.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/oracle/cx_oracle.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/oracle/provision.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/__pycache__/array.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/__pycache__/asyncpg.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/__pycache__/base.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/__pycache__/dml.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/__pycache__/ext.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/__pycache__/hstore.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/__pycache__/json.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/__pycache__/pg8000.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/__pycache__/provision.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/__pycache__/psycopg2.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/__pycache__/psycopg2cffi.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/__pycache__/pygresql.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/__pycache__/pypostgresql.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/__pycache__/ranges.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/array.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/asyncpg.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/base.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/dml.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/ext.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/hstore.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/json.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/pg8000.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/provision.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/psycopg2.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/psycopg2cffi.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/pygresql.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/pypostgresql.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/postgresql/ranges.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sqlite/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sqlite/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sqlite/__pycache__/aiosqlite.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sqlite/__pycache__/base.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sqlite/__pycache__/dml.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sqlite/__pycache__/json.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sqlite/__pycache__/provision.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sqlite/__pycache__/pysqlcipher.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sqlite/__pycache__/pysqlite.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sqlite/aiosqlite.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sqlite/base.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sqlite/dml.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sqlite/json.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sqlite/provision.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sqlite/pysqlcipher.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sqlite/pysqlite.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sybase/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sybase/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sybase/__pycache__/base.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sybase/__pycache__/mxodbc.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sybase/__pycache__/pyodbc.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sybase/__pycache__/pysybase.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sybase/base.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sybase/mxodbc.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sybase/pyodbc.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/dialects/sybase/pysybase.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/__pycache__/base.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/__pycache__/characteristics.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/__pycache__/create.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/__pycache__/cursor.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/__pycache__/default.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/__pycache__/events.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/__pycache__/interfaces.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/__pycache__/mock.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/__pycache__/reflection.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/__pycache__/result.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/__pycache__/row.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/__pycache__/strategies.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/__pycache__/url.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/__pycache__/util.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/base.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/characteristics.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/create.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/cursor.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/default.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/events.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/interfaces.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/mock.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/reflection.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/result.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/row.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/strategies.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/url.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/engine/util.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/event/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/event/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/event/__pycache__/api.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/event/__pycache__/attr.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/event/__pycache__/base.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/event/__pycache__/legacy.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/event/__pycache__/registry.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/event/api.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/event/attr.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/event/base.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/event/legacy.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/event/registry.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/events.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/exc.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/__pycache__/associationproxy.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/__pycache__/automap.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/__pycache__/baked.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/__pycache__/compiler.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/__pycache__/horizontal_shard.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/__pycache__/hybrid.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/__pycache__/indexable.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/__pycache__/instrumentation.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/__pycache__/mutable.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/__pycache__/orderinglist.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/__pycache__/serializer.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/associationproxy.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/asyncio/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/asyncio/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/asyncio/__pycache__/base.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/asyncio/__pycache__/engine.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/asyncio/__pycache__/events.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/asyncio/__pycache__/exc.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/asyncio/__pycache__/result.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/asyncio/__pycache__/scoping.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/asyncio/__pycache__/session.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/asyncio/base.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/asyncio/engine.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/asyncio/events.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/asyncio/exc.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/asyncio/result.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/asyncio/scoping.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/asyncio/session.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/automap.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/baked.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/compiler.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/declarative/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/declarative/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/declarative/__pycache__/extensions.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/declarative/extensions.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/horizontal_shard.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/hybrid.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/indexable.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/instrumentation.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/mutable.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/mypy/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/mypy/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/mypy/__pycache__/apply.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/mypy/__pycache__/decl_class.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/mypy/__pycache__/infer.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/mypy/__pycache__/names.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/mypy/__pycache__/plugin.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/mypy/__pycache__/util.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/mypy/apply.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/mypy/decl_class.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/mypy/infer.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/mypy/names.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/mypy/plugin.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/mypy/util.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/orderinglist.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/ext/serializer.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/future/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/future/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/future/__pycache__/engine.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/future/engine.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/future/orm/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/future/orm/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/inspection.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/log.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/attributes.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/base.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/clsregistry.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/collections.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/context.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/decl_api.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/decl_base.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/dependency.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/descriptor_props.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/dynamic.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/evaluator.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/events.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/exc.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/identity.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/instrumentation.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/interfaces.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/loading.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/mapper.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/path_registry.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/persistence.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/properties.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/query.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/relationships.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/scoping.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/session.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/state.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/strategies.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/strategy_options.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/sync.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/unitofwork.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/__pycache__/util.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/attributes.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/base.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/clsregistry.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/collections.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/context.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/decl_api.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/decl_base.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/dependency.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/descriptor_props.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/dynamic.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/evaluator.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/events.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/exc.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/identity.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/instrumentation.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/interfaces.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/loading.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/mapper.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/path_registry.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/persistence.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/properties.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/query.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/relationships.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/scoping.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/session.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/state.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/strategies.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/strategy_options.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/sync.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/unitofwork.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/orm/util.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/pool/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/pool/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/pool/__pycache__/base.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/pool/__pycache__/dbapi_proxy.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/pool/__pycache__/events.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/pool/__pycache__/impl.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/pool/base.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/pool/dbapi_proxy.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/pool/events.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/pool/impl.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/processors.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/schema.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/annotation.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/base.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/coercions.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/compiler.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/crud.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/ddl.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/default_comparator.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/dml.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/elements.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/events.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/expression.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/functions.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/lambdas.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/naming.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/operators.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/roles.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/schema.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/selectable.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/sqltypes.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/traversals.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/type_api.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/util.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/__pycache__/visitors.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/annotation.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/base.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/coercions.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/compiler.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/crud.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/ddl.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/default_comparator.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/dml.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/elements.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/events.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/expression.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/functions.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/lambdas.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/naming.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/operators.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/roles.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/schema.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/selectable.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/sqltypes.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/traversals.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/type_api.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/util.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/sql/visitors.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/__pycache__/assertions.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/__pycache__/assertsql.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/__pycache__/asyncio.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/__pycache__/config.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/__pycache__/engines.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/__pycache__/entities.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/__pycache__/exclusions.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/__pycache__/fixtures.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/__pycache__/mock.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/__pycache__/pickleable.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/__pycache__/profiling.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/__pycache__/provision.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/__pycache__/requirements.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/__pycache__/schema.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/__pycache__/util.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/__pycache__/warnings.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/assertions.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/assertsql.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/asyncio.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/config.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/engines.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/entities.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/exclusions.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/fixtures.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/mock.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/pickleable.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/plugin/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/plugin/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/plugin/__pycache__/bootstrap.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/plugin/__pycache__/plugin_base.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/plugin/__pycache__/pytestplugin.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/plugin/__pycache__/reinvent_fixtures_py2k.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/plugin/bootstrap.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/plugin/plugin_base.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/plugin/pytestplugin.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/plugin/reinvent_fixtures_py2k.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/profiling.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/provision.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/requirements.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/schema.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/__pycache__/test_cte.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/__pycache__/test_ddl.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/__pycache__/test_deprecations.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/__pycache__/test_dialect.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/__pycache__/test_insert.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/__pycache__/test_reflection.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/__pycache__/test_results.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/__pycache__/test_rowcount.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/__pycache__/test_select.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/__pycache__/test_sequence.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/__pycache__/test_types.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/__pycache__/test_unicode_ddl.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/__pycache__/test_update_delete.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/test_cte.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/test_ddl.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/test_deprecations.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/test_dialect.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/test_insert.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/test_reflection.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/test_results.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/test_rowcount.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/test_select.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/test_sequence.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/test_types.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/test_unicode_ddl.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/suite/test_update_delete.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/util.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/testing/warnings.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/types.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/util/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/util/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/util/__pycache__/_collections.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/util/__pycache__/_compat_py3k.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/util/__pycache__/_concurrency_py3k.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/util/__pycache__/_preloaded.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/util/__pycache__/compat.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/util/__pycache__/concurrency.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/util/__pycache__/deprecations.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/util/__pycache__/langhelpers.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/util/__pycache__/queue.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/util/__pycache__/topological.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/util/_collections.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/util/_compat_py3k.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/util/_concurrency_py3k.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/util/_preloaded.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/util/compat.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/util/concurrency.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/util/deprecations.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/util/langhelpers.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/util/queue.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/sqlalchemy/util/topological.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity-8.0.1.dist-info/AUTHORS create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity-8.0.1.dist-info/INSTALLER create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity-8.0.1.dist-info/LICENSE create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity-8.0.1.dist-info/METADATA create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity-8.0.1.dist-info/RECORD create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity-8.0.1.dist-info/WHEEL create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity-8.0.1.dist-info/pbr.json create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity-8.0.1.dist-info/top_level.txt create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/__pycache__/_asyncio.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/__pycache__/_utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/__pycache__/after.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/__pycache__/before.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/__pycache__/before_sleep.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/__pycache__/nap.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/__pycache__/retry.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/__pycache__/stop.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/__pycache__/tornadoweb.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/__pycache__/wait.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/_asyncio.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/_utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/after.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/before.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/before_sleep.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/nap.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/py.typed create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/retry.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/stop.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/tornadoweb.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/tenacity/wait.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/_internal.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/_reloader.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/datastructures.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/exceptions.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/filesystem.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/formparser.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/http.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/local.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/routing.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/security.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/serving.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/test.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/testapp.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/urls.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/user_agent.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/useragents.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/__pycache__/wsgi.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/_internal.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/_reloader.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/datastructures.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/datastructures.pyi create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/debug/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/debug/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/debug/__pycache__/console.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/debug/__pycache__/repr.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/debug/__pycache__/tbtools.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/debug/console.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/debug/repr.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/FONT_LICENSE create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/ICON_LICENSE.md create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/console.png create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/debugger.js create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/less.png create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/more.png create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/source.png create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/style.css create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/debug/shared/ubuntu.ttf create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/debug/tbtools.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/exceptions.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/filesystem.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/formparser.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/http.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/local.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/dispatcher.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/http_proxy.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/lint.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/profiler.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/proxy_fix.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/middleware/__pycache__/shared_data.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/middleware/dispatcher.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/middleware/http_proxy.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/middleware/lint.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/middleware/profiler.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/middleware/proxy_fix.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/middleware/shared_data.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/py.typed create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/routing.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/multipart.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/request.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/response.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/sansio/__pycache__/utils.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/sansio/multipart.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/sansio/request.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/sansio/response.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/sansio/utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/security.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/serving.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/test.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/testapp.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/urls.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/user_agent.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/useragents.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/utils.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__init__.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__pycache__/__init__.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__pycache__/accept.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__pycache__/auth.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__pycache__/base_request.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__pycache__/base_response.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__pycache__/common_descriptors.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__pycache__/cors.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__pycache__/etag.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__pycache__/json.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__pycache__/request.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__pycache__/response.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/__pycache__/user_agent.cpython-38.pyc create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/accept.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/auth.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/base_request.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/base_response.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/common_descriptors.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/cors.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/etag.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/json.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/request.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/response.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wrappers/user_agent.py create mode 100644 flask-app/venv/lib/python3.8/site-packages/werkzeug/wsgi.py create mode 100644 flask-app/venv/pyvenv.cfg create mode 100755 screenshots/APM Datadog Yaml port 5050.png create mode 100755 screenshots/APM Datadog Yaml.png create mode 100755 screenshots/APM My to do app on the browser.png create mode 100755 screenshots/APM dashboard on datadog website Get results.png create mode 100755 screenshots/APM datadog dashboard 1.png create mode 100755 screenshots/APM ddtrace python app run.png create mode 100755 screenshots/APM my to do app showing entries.png create mode 100755 screenshots/Agent Reporting Metrics.png create mode 100755 screenshots/Alert Email.png create mode 100755 screenshots/Configure the monitor Message.png create mode 100755 screenshots/Datadog Agent Yaml Tags 1.png create mode 100755 screenshots/Datadog Dashboard Tags.png create mode 100755 screenshots/Datadog Yaml Tags 2.png create mode 100755 screenshots/Downtime Weekday Settings.png create mode 100755 screenshots/Downtime Weekend settings.png create mode 100755 screenshots/Downtime monitor email on the weekend.png create mode 100755 screenshots/Downtime setting dashboard.png create mode 100755 screenshots/Downtime settings weekday dashboard .png create mode 100755 screenshots/Downtime settings weekend dashboard.png create mode 100755 screenshots/Email notice of snapshot anamolies.png create mode 100755 screenshots/Metric Monitor Json.png create mode 100755 screenshots/Missing Data Email.png create mode 100755 screenshots/Monitor metric Dashboard Timeboard.png create mode 100755 screenshots/My Hourly Timeboard.png create mode 100755 screenshots/Postgres conf.d.png create mode 100755 screenshots/Postman API Editor.png create mode 100755 screenshots/Warn Email.png create mode 100755 screenshots/mymetric Dashboard 45 Interval.png create mode 100755 screenshots/mymetric Dashboard.png create mode 100755 screenshots/mymetric sum.png create mode 100755 screenshots/mymetric yaml 45 interval.png create mode 100755 screenshots/mymetric yaml.png create mode 100755 screenshots/mymetric.py.png diff --git a/flask-app/__pycache__/app.cpython-36.pyc b/flask-app/__pycache__/app.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..17d6d7b62cf9d3c9edabd367c9fb7f8fb6f3c99e GIT binary patch literal 1899 zcma)7OLH4V5T4nGc31LKekI_TghxOJtSaC@xm>9@P7E#*8Deu_FIy}1SYCM_^6Utg zT+Ye4>Id)>_znD%xl%<{_z5}DJ(6V?p@3CwZ%*shdi@FE3-p8b3!L`LzmN>l~yt@^o(w&einoQXM&6B6%T9Rh+1z3 z+-(LGqj}p*c%p`Lv)?fAA$ZLo5VO6xD?6M=j|ISWP=lG*41E@jWftbT9Og%s2zqr` zfJLao5-g7#Sbs#;qwLj z8QWXloF<6l6eVbv{8mvY*5f_vioIgW27Za9JF*bI-MR?caVa)WRF)!dfx=`&X;P|& zV}fcIIhR9i$7w2U4AkI~;@zBEF5^%;Cq*XaQ7dU%W>vYoi{A^U83SnN`-4MFvGy{d zP6{Y5W{;`HHm@tnHN%U5-0stg&^{OKhONC)$e)vrpqeX-f$WGdDEo09sbODC(OS6E z6zvuc#%KYXgYXhsut(M_uAH9Rt6ZX;GoyRd3d&ajI8-215G}cg5%wCE`N}#D43tJy z(jMBude5}o70M(?&XEz%S(ZtWW1bLh1^LYqCGl!ul31Dq3;kT2N0j2frB8l{PL#)z zh-LN+ryu5!>=!G|K_AOPG`EvnZ5f6Vc+??|=^^yQjN$qVRL1BLH6QVwjhypK-g9Qy zC6641_D}U{)QGBG%7PN30;IGyV_OuW-S;SpS9N|3)i6#+dRG5>tS>GCtBf zK;=X7`;^4Ai%mDk2HGgn6rdrKtaPDdags+7^@9tdJ?OR?cjP)nAtp_enM%9KX}Dne zBf|SL9$&GN{-r_>V0_O~9%T&$)?dF*^_L$fT>~L>Ule%$Ni)l0fLu~%YUf") +def update(todo_id): + todo = Todo.query.filter_by(id=todo_id).first() + todo.complete = not todo.complete + db.session.commit() + return redirect(url_for("home")) + +@app.route("/delete/") +def delete(todo_id): + todo = Todo.query.filter_by(id=todo_id).first() + db.session.delete(todo) + db.session.commit() + return redirect(url_for("home")) + +if __name__ == "__main__": + db.create_all() + app.run(debug=True) + app.run(host='0.0.0.0', port='5000') diff --git a/flask-app/db.sqlite b/flask-app/db.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..60d0725f78a03296df0b2ae25c4cd03a1ff6afd7 GIT binary patch literal 8192 zcmeI%KTE?v6aer`jkXDB>QF=|_#DI*L~0$J1k*HVF->b4g$@p9bJnBzM{+6-4&D78 zu5Nx9zk{E_i_qD{t?!XXa*xYh^2_wd=*r=O!F`fNRKPK*5=9|r0ECdX%#zGSDDpyG z4CJ{O{}pX=SpQg;vlL@a`9@hKyIMB^hd#zZ_Ohd@#L}!qi{GV!2FVo6JZE_x$vfl|Z&p;Yul4DS Y0T=L`WHYF)s$@^|SuW&uV5TYj06ddfn*aa+ literal 0 HcmV?d00001 diff --git a/flask-app/templates/base.html b/flask-app/templates/base.html new file mode 100644 index 000000000..b40815c08 --- /dev/null +++ b/flask-app/templates/base.html @@ -0,0 +1,44 @@ + + + + + + + Todo App + + + + + + + + + + \ No newline at end of file diff --git a/flask-app/venv/bin/Activate.ps1 b/flask-app/venv/bin/Activate.ps1 new file mode 100644 index 000000000..5b9adb2b7 --- /dev/null +++ b/flask-app/venv/bin/Activate.ps1 @@ -0,0 +1,231 @@ +<# +.Synopsis +Activate a Python virtual environment for the current Powershell session. + +.Description +Pushes the python executable for a virtual environment to the front of the +$Env:PATH environment variable and sets the prompt to signify that you are +in a Python virtual environment. Makes use of the command line switches as +well as the `pyvenv.cfg` file values present in the virtual environment. + +.Parameter VenvDir +Path to the directory that contains the virtual environment to activate. The +default value for this is the parent of the directory that the Activate.ps1 +script is located within. + +.Parameter Prompt +The prompt prefix to display when this virtual environment is activated. By +default, this prompt is the name of the virtual environment folder (VenvDir) +surrounded by parentheses and followed by a single space (ie. '(.venv) '). + +.Example +Activate.ps1 +Activates the Python virtual environment that contains the Activate.ps1 script. + +.Example +Activate.ps1 -Verbose +Activates the Python virtual environment that contains the Activate.ps1 script, +and shows extra information about the activation as it executes. + +.Example +Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv +Activates the Python virtual environment located in the specified location. + +.Example +Activate.ps1 -Prompt "MyPython" +Activates the Python virtual environment that contains the Activate.ps1 script, +and prefixes the current prompt with the specified string (surrounded in +parentheses) while the virtual environment is active. + + +#> +Param( + [Parameter(Mandatory = $false)] + [String] + $VenvDir, + [Parameter(Mandatory = $false)] + [String] + $Prompt +) + +<# Function declarations --------------------------------------------------- #> + +<# +.Synopsis +Remove all shell session elements added by the Activate script, including the +addition of the virtual environment's Python executable from the beginning of +the PATH variable. + +.Parameter NonDestructive +If present, do not remove this function from the global namespace for the +session. + +#> +function global:deactivate ([switch]$NonDestructive) { + # Revert to original values + + # The prior prompt: + if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) { + Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt + Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT + } + + # The prior PYTHONHOME: + if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) { + Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME + Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME + } + + # The prior PATH: + if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) { + Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH + Remove-Item -Path Env:_OLD_VIRTUAL_PATH + } + + # Just remove the VIRTUAL_ENV altogether: + if (Test-Path -Path Env:VIRTUAL_ENV) { + Remove-Item -Path env:VIRTUAL_ENV + } + + # Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether: + if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) { + Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force + } + + # Leave deactivate function in the global namespace if requested: + if (-not $NonDestructive) { + Remove-Item -Path function:deactivate + } +} + +<# +.Description +Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the +given folder, and returns them in a map. + +For each line in the pyvenv.cfg file, if that line can be parsed into exactly +two strings separated by `=` (with any amount of whitespace surrounding the =) +then it is considered a `key = value` line. The left hand string is the key, +the right hand is the value. + +If the value starts with a `'` or a `"` then the first and last character is +stripped from the value before being captured. + +.Parameter ConfigDir +Path to the directory that contains the `pyvenv.cfg` file. +#> +function Get-PyVenvConfig( + [String] + $ConfigDir +) { + Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg" + + # Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue). + $pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue + + # An empty map will be returned if no config file is found. + $pyvenvConfig = @{ } + + if ($pyvenvConfigPath) { + + Write-Verbose "File exists, parse `key = value` lines" + $pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath + + $pyvenvConfigContent | ForEach-Object { + $keyval = $PSItem -split "\s*=\s*", 2 + if ($keyval[0] -and $keyval[1]) { + $val = $keyval[1] + + # Remove extraneous quotations around a string value. + if ("'""".Contains($val.Substring(0,1))) { + $val = $val.Substring(1, $val.Length - 2) + } + + $pyvenvConfig[$keyval[0]] = $val + Write-Verbose "Adding Key: '$($keyval[0])'='$val'" + } + } + } + return $pyvenvConfig +} + + +<# Begin Activate script --------------------------------------------------- #> + +# Determine the containing directory of this script +$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition +$VenvExecDir = Get-Item -Path $VenvExecPath + +Write-Verbose "Activation script is located in path: '$VenvExecPath'" +Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)" +Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)" + +# Set values required in priority: CmdLine, ConfigFile, Default +# First, get the location of the virtual environment, it might not be +# VenvExecDir if specified on the command line. +if ($VenvDir) { + Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values" +} else { + Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir." + $VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/") + $VenvDir = $VenvDir.Insert($VenvDir.Length, "/") + Write-Verbose "VenvDir=$VenvDir" +} + +# Next, read the `pyvenv.cfg` file to determine any required value such +# as `prompt`. +$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir + +# Next, set the prompt from the command line, or the config file, or +# just use the name of the virtual environment folder. +if ($Prompt) { + Write-Verbose "Prompt specified as argument, using '$Prompt'" +} else { + Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value" + if ($pyvenvCfg -and $pyvenvCfg['prompt']) { + Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'" + $Prompt = $pyvenvCfg['prompt']; + } + else { + Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virutal environment)" + Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'" + $Prompt = Split-Path -Path $venvDir -Leaf + } +} + +Write-Verbose "Prompt = '$Prompt'" +Write-Verbose "VenvDir='$VenvDir'" + +# Deactivate any currently active virtual environment, but leave the +# deactivate function in place. +deactivate -nondestructive + +# Now set the environment variable VIRTUAL_ENV, used by many tools to determine +# that there is an activated venv. +$env:VIRTUAL_ENV = $VenvDir + +if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) { + + Write-Verbose "Setting prompt to '$Prompt'" + + # Set the prompt to include the env name + # Make sure _OLD_VIRTUAL_PROMPT is global + function global:_OLD_VIRTUAL_PROMPT { "" } + Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT + New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt + + function global:prompt { + Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) " + _OLD_VIRTUAL_PROMPT + } +} + +# Clear PYTHONHOME +if (Test-Path -Path Env:PYTHONHOME) { + Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME + Remove-Item -Path Env:PYTHONHOME +} + +# Add the venv to the PATH +Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH +$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH" diff --git a/flask-app/venv/bin/activate b/flask-app/venv/bin/activate new file mode 100644 index 000000000..83c50e639 --- /dev/null +++ b/flask-app/venv/bin/activate @@ -0,0 +1,76 @@ +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + +deactivate () { + # reset old environment variables + if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then + PATH="${_OLD_VIRTUAL_PATH:-}" + export PATH + unset _OLD_VIRTUAL_PATH + fi + if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then + PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}" + export PYTHONHOME + unset _OLD_VIRTUAL_PYTHONHOME + fi + + # This should detect bash and zsh, which have a hash command that must + # be called to get it to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r + fi + + if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then + PS1="${_OLD_VIRTUAL_PS1:-}" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + unset VIRTUAL_ENV + if [ ! "${1:-}" = "nondestructive" ] ; then + # Self destruct! + unset -f deactivate + fi +} + +# unset irrelevant variables +deactivate nondestructive + +VIRTUAL_ENV="/Users/Kate/Datadog/myapp/venv" +export VIRTUAL_ENV + +_OLD_VIRTUAL_PATH="$PATH" +PATH="$VIRTUAL_ENV/bin:$PATH" +export PATH + +# unset PYTHONHOME if set +# this will fail if PYTHONHOME is set to the empty string (which is bad anyway) +# could use `if (set -u; : $PYTHONHOME) ;` in bash +if [ -n "${PYTHONHOME:-}" ] ; then + _OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}" + unset PYTHONHOME +fi + +if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then + _OLD_VIRTUAL_PS1="${PS1:-}" + if [ "x(venv) " != x ] ; then + PS1="(venv) ${PS1:-}" + else + if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1" + else + PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1" + fi + fi + export PS1 +fi + +# This should detect bash and zsh, which have a hash command that must +# be called to get it to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then + hash -r +fi diff --git a/flask-app/venv/bin/activate.csh b/flask-app/venv/bin/activate.csh new file mode 100644 index 000000000..b8f8cd76f --- /dev/null +++ b/flask-app/venv/bin/activate.csh @@ -0,0 +1,37 @@ +# This file must be used with "source bin/activate.csh" *from csh*. +# You cannot run it directly. +# Created by Davide Di Blasi . +# Ported to Python 3.3 venv by Andrew Svetlov + +alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; test "\!:*" != "nondestructive" && unalias deactivate' + +# Unset irrelevant variables. +deactivate nondestructive + +setenv VIRTUAL_ENV "/Users/Kate/Datadog/myapp/venv" + +set _OLD_VIRTUAL_PATH="$PATH" +setenv PATH "$VIRTUAL_ENV/bin:$PATH" + + +set _OLD_VIRTUAL_PROMPT="$prompt" + +if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then + if ("venv" != "") then + set env_name = "venv" + else + if (`basename "VIRTUAL_ENV"` == "__") then + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + set env_name = `basename \`dirname "$VIRTUAL_ENV"\`` + else + set env_name = `basename "$VIRTUAL_ENV"` + endif + endif + set prompt = "[$env_name] $prompt" + unset env_name +endif + +alias pydoc python -m pydoc + +rehash diff --git a/flask-app/venv/bin/activate.fish b/flask-app/venv/bin/activate.fish new file mode 100644 index 000000000..84e7d785e --- /dev/null +++ b/flask-app/venv/bin/activate.fish @@ -0,0 +1,75 @@ +# This file must be used with ". bin/activate.fish" *from fish* (http://fishshell.org) +# you cannot run it directly + +function deactivate -d "Exit virtualenv and return to normal shell environment" + # reset old environment variables + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + if test -n "$_OLD_VIRTUAL_PYTHONHOME" + set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME + set -e _OLD_VIRTUAL_PYTHONHOME + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + functions -e fish_prompt + set -e _OLD_FISH_PROMPT_OVERRIDE + functions -c _old_fish_prompt fish_prompt + functions -e _old_fish_prompt + end + + set -e VIRTUAL_ENV + if test "$argv[1]" != "nondestructive" + # Self destruct! + functions -e deactivate + end +end + +# unset irrelevant variables +deactivate nondestructive + +set -gx VIRTUAL_ENV "/Users/Kate/Datadog/myapp/venv" + +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx PATH "$VIRTUAL_ENV/bin" $PATH + +# unset PYTHONHOME if set +if set -q PYTHONHOME + set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME + set -e PYTHONHOME +end + +if test -z "$VIRTUAL_ENV_DISABLE_PROMPT" + # fish uses a function instead of an env var to generate the prompt. + + # save the current fish_prompt function as the function _old_fish_prompt + functions -c fish_prompt _old_fish_prompt + + # with the original prompt function renamed, we can override with our own. + function fish_prompt + # Save the return status of the last command + set -l old_status $status + + # Prompt override? + if test -n "(venv) " + printf "%s%s" "(venv) " (set_color normal) + else + # ...Otherwise, prepend env + set -l _checkbase (basename "$VIRTUAL_ENV") + if test $_checkbase = "__" + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + printf "%s[%s]%s " (set_color -b blue white) (basename (dirname "$VIRTUAL_ENV")) (set_color normal) + else + printf "%s(%s)%s" (set_color -b blue white) (basename "$VIRTUAL_ENV") (set_color normal) + end + end + + # Restore the return status of the previous command. + echo "exit $old_status" | . + _old_fish_prompt + end + + set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV" +end diff --git a/flask-app/venv/bin/ddtrace-run b/flask-app/venv/bin/ddtrace-run new file mode 100755 index 000000000..c36cae3a3 --- /dev/null +++ b/flask-app/venv/bin/ddtrace-run @@ -0,0 +1,10 @@ +#!/Users/Kate/Datadog/myapp/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from ddtrace.commands.ddtrace_run import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/flask-app/venv/bin/easy_install b/flask-app/venv/bin/easy_install new file mode 100755 index 000000000..b4a4d31fc --- /dev/null +++ b/flask-app/venv/bin/easy_install @@ -0,0 +1,10 @@ +#!/Users/Kate/Datadog/myapp/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from setuptools.command.easy_install import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/flask-app/venv/bin/easy_install-3.8 b/flask-app/venv/bin/easy_install-3.8 new file mode 100755 index 000000000..b4a4d31fc --- /dev/null +++ b/flask-app/venv/bin/easy_install-3.8 @@ -0,0 +1,10 @@ +#!/Users/Kate/Datadog/myapp/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from setuptools.command.easy_install import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/flask-app/venv/bin/flask b/flask-app/venv/bin/flask new file mode 100755 index 000000000..8e24564b3 --- /dev/null +++ b/flask-app/venv/bin/flask @@ -0,0 +1,10 @@ +#!/Users/Kate/Datadog/myapp/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from flask.cli import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/flask-app/venv/bin/pip b/flask-app/venv/bin/pip new file mode 100755 index 000000000..f30b86249 --- /dev/null +++ b/flask-app/venv/bin/pip @@ -0,0 +1,10 @@ +#!/Users/Kate/Datadog/myapp/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from pip._internal import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/flask-app/venv/bin/pip3 b/flask-app/venv/bin/pip3 new file mode 100755 index 000000000..f30b86249 --- /dev/null +++ b/flask-app/venv/bin/pip3 @@ -0,0 +1,10 @@ +#!/Users/Kate/Datadog/myapp/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from pip._internal import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/flask-app/venv/bin/pip3.8 b/flask-app/venv/bin/pip3.8 new file mode 100755 index 000000000..f30b86249 --- /dev/null +++ b/flask-app/venv/bin/pip3.8 @@ -0,0 +1,10 @@ +#!/Users/Kate/Datadog/myapp/venv/bin/python3 +# -*- coding: utf-8 -*- +import re +import sys + +from pip._internal import main + +if __name__ == '__main__': + sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) + sys.exit(main()) diff --git a/flask-app/venv/bin/python b/flask-app/venv/bin/python new file mode 120000 index 000000000..b8a0adbbb --- /dev/null +++ b/flask-app/venv/bin/python @@ -0,0 +1 @@ +python3 \ No newline at end of file diff --git a/flask-app/venv/bin/python3 b/flask-app/venv/bin/python3 new file mode 120000 index 000000000..3b8210b56 --- /dev/null +++ b/flask-app/venv/bin/python3 @@ -0,0 +1 @@ +/Library/Frameworks/Python.framework/Versions/3.8/bin/python3 \ No newline at end of file diff --git a/flask-app/venv/include/site/python3.8/greenlet/greenlet.h b/flask-app/venv/include/site/python3.8/greenlet/greenlet.h new file mode 100644 index 000000000..830bef8dd --- /dev/null +++ b/flask-app/venv/include/site/python3.8/greenlet/greenlet.h @@ -0,0 +1,146 @@ +/* -*- indent-tabs-mode: nil; tab-width: 4; -*- */ + +/* Greenlet object interface */ + +#ifndef Py_GREENLETOBJECT_H +#define Py_GREENLETOBJECT_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* This is deprecated and undocumented. It does not change. */ +#define GREENLET_VERSION "1.0.0" + +typedef struct _greenlet { + PyObject_HEAD + char* stack_start; + char* stack_stop; + char* stack_copy; + intptr_t stack_saved; + struct _greenlet* stack_prev; + struct _greenlet* parent; + PyObject* run_info; + struct _frame* top_frame; + int recursion_depth; + PyObject* weakreflist; +#if PY_VERSION_HEX >= 0x030700A3 + _PyErr_StackItem* exc_info; + _PyErr_StackItem exc_state; +#else + PyObject* exc_type; + PyObject* exc_value; + PyObject* exc_traceback; +#endif + PyObject* dict; +#if PY_VERSION_HEX >= 0x030700A3 + PyObject* context; +#endif +#if PY_VERSION_HEX >= 0x30A00B1 + CFrame* cframe; +#endif +} PyGreenlet; + +#define PyGreenlet_Check(op) PyObject_TypeCheck(op, &PyGreenlet_Type) +#define PyGreenlet_MAIN(op) (((PyGreenlet*)(op))->stack_stop == (char*)-1) +#define PyGreenlet_STARTED(op) (((PyGreenlet*)(op))->stack_stop != NULL) +#define PyGreenlet_ACTIVE(op) (((PyGreenlet*)(op))->stack_start != NULL) +#define PyGreenlet_GET_PARENT(op) (((PyGreenlet*)(op))->parent) + +/* C API functions */ + +/* Total number of symbols that are exported */ +#define PyGreenlet_API_pointers 8 + +#define PyGreenlet_Type_NUM 0 +#define PyExc_GreenletError_NUM 1 +#define PyExc_GreenletExit_NUM 2 + +#define PyGreenlet_New_NUM 3 +#define PyGreenlet_GetCurrent_NUM 4 +#define PyGreenlet_Throw_NUM 5 +#define PyGreenlet_Switch_NUM 6 +#define PyGreenlet_SetParent_NUM 7 + +#ifndef GREENLET_MODULE +/* This section is used by modules that uses the greenlet C API */ +static void** _PyGreenlet_API = NULL; + +# define PyGreenlet_Type \ + (*(PyTypeObject*)_PyGreenlet_API[PyGreenlet_Type_NUM]) + +# define PyExc_GreenletError \ + ((PyObject*)_PyGreenlet_API[PyExc_GreenletError_NUM]) + +# define PyExc_GreenletExit \ + ((PyObject*)_PyGreenlet_API[PyExc_GreenletExit_NUM]) + +/* + * PyGreenlet_New(PyObject *args) + * + * greenlet.greenlet(run, parent=None) + */ +# define PyGreenlet_New \ + (*(PyGreenlet * (*)(PyObject * run, PyGreenlet * parent)) \ + _PyGreenlet_API[PyGreenlet_New_NUM]) + +/* + * PyGreenlet_GetCurrent(void) + * + * greenlet.getcurrent() + */ +# define PyGreenlet_GetCurrent \ + (*(PyGreenlet * (*)(void)) _PyGreenlet_API[PyGreenlet_GetCurrent_NUM]) + +/* + * PyGreenlet_Throw( + * PyGreenlet *greenlet, + * PyObject *typ, + * PyObject *val, + * PyObject *tb) + * + * g.throw(...) + */ +# define PyGreenlet_Throw \ + (*(PyObject * (*)(PyGreenlet * self, \ + PyObject * typ, \ + PyObject * val, \ + PyObject * tb)) \ + _PyGreenlet_API[PyGreenlet_Throw_NUM]) + +/* + * PyGreenlet_Switch(PyGreenlet *greenlet, PyObject *args) + * + * g.switch(*args, **kwargs) + */ +# define PyGreenlet_Switch \ + (*(PyObject * \ + (*)(PyGreenlet * greenlet, PyObject * args, PyObject * kwargs)) \ + _PyGreenlet_API[PyGreenlet_Switch_NUM]) + +/* + * PyGreenlet_SetParent(PyObject *greenlet, PyObject *new_parent) + * + * g.parent = new_parent + */ +# define PyGreenlet_SetParent \ + (*(int (*)(PyGreenlet * greenlet, PyGreenlet * nparent)) \ + _PyGreenlet_API[PyGreenlet_SetParent_NUM]) + +/* Macro that imports greenlet and initializes C API */ +/* NOTE: This has actually moved to ``greenlet._greenlet._C_API``, but we + keep the older definition to be sure older code that might have a copy of + the header still works. */ +# define PyGreenlet_Import() \ + { \ + _PyGreenlet_API = (void**)PyCapsule_Import("greenlet._C_API", 0); \ + } + +#endif /* GREENLET_MODULE */ + +#ifdef __cplusplus +} +#endif +#endif /* !Py_GREENLETOBJECT_H */ diff --git a/flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/INSTALLER b/flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/LICENSE.rst b/flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/LICENSE.rst new file mode 100644 index 000000000..9d227a0cc --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2010 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/METADATA b/flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/METADATA new file mode 100644 index 000000000..aaf27caed --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/METADATA @@ -0,0 +1,125 @@ +Metadata-Version: 2.1 +Name: Flask +Version: 2.0.2 +Summary: A simple framework for building complex web applications. +Home-page: https://palletsprojects.com/p/flask +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://flask.palletsprojects.com/ +Project-URL: Changes, https://flask.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/flask/ +Project-URL: Issue Tracker, https://github.com/pallets/flask/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Framework :: Flask +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application +Classifier: Topic :: Software Development :: Libraries :: Application Frameworks +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: Werkzeug (>=2.0) +Requires-Dist: Jinja2 (>=3.0) +Requires-Dist: itsdangerous (>=2.0) +Requires-Dist: click (>=7.1.2) +Provides-Extra: async +Requires-Dist: asgiref (>=3.2) ; extra == 'async' +Provides-Extra: dotenv +Requires-Dist: python-dotenv ; extra == 'dotenv' + +Flask +===== + +Flask is a lightweight `WSGI`_ web application framework. It is designed +to make getting started quick and easy, with the ability to scale up to +complex applications. It began as a simple wrapper around `Werkzeug`_ +and `Jinja`_ and has become one of the most popular Python web +application frameworks. + +Flask offers suggestions, but doesn't enforce any dependencies or +project layout. It is up to the developer to choose the tools and +libraries they want to use. There are many extensions provided by the +community that make adding new functionality easy. + +.. _WSGI: https://wsgi.readthedocs.io/ +.. _Werkzeug: https://werkzeug.palletsprojects.com/ +.. _Jinja: https://jinja.palletsprojects.com/ + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U Flask + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +A Simple Example +---------------- + +.. code-block:: python + + # save this as app.py + from flask import Flask + + app = Flask(__name__) + + @app.route("/") + def hello(): + return "Hello, World!" + +.. code-block:: text + + $ flask run + * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) + + +Contributing +------------ + +For guidance on setting up a development environment and how to make a +contribution to Flask, see the `contributing guidelines`_. + +.. _contributing guidelines: https://github.com/pallets/flask/blob/main/CONTRIBUTING.rst + + +Donate +------ + +The Pallets organization develops and supports Flask and the libraries +it uses. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, `please +donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://flask.palletsprojects.com/ +- Changes: https://flask.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/Flask/ +- Source Code: https://github.com/pallets/flask/ +- Issue Tracker: https://github.com/pallets/flask/issues/ +- Website: https://palletsprojects.com/p/flask/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/RECORD b/flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/RECORD new file mode 100644 index 000000000..c87e92b92 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/RECORD @@ -0,0 +1,51 @@ +../../../bin/flask,sha256=xBFcdARdhz83LcheIPBWDYpWv0OTH3qBfBco8aJQ9aI,237 +Flask-2.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Flask-2.0.2.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +Flask-2.0.2.dist-info/METADATA,sha256=aKsvjFA_ZjZN1jLh1Ac3aQk-ZUZDPrrwo_TGYW1kdAQ,3839 +Flask-2.0.2.dist-info/RECORD,, +Flask-2.0.2.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 +Flask-2.0.2.dist-info/entry_points.txt,sha256=gBLA1aKg0OYR8AhbAfg8lnburHtKcgJLDU52BBctN0k,42 +Flask-2.0.2.dist-info/top_level.txt,sha256=dvi65F6AeGWVU0TBpYiC04yM60-FX1gJFkK31IKQr5c,6 +flask/__init__.py,sha256=9ZCelLoNCpr6eSuLmYlzvbp12B3lrLgoN5U2UWk1vdo,2251 +flask/__main__.py,sha256=bYt9eEaoRQWdejEHFD8REx9jxVEdZptECFsV7F49Ink,30 +flask/__pycache__/__init__.cpython-38.pyc,, +flask/__pycache__/__main__.cpython-38.pyc,, +flask/__pycache__/app.cpython-38.pyc,, +flask/__pycache__/blueprints.cpython-38.pyc,, +flask/__pycache__/cli.cpython-38.pyc,, +flask/__pycache__/config.cpython-38.pyc,, +flask/__pycache__/ctx.cpython-38.pyc,, +flask/__pycache__/debughelpers.cpython-38.pyc,, +flask/__pycache__/globals.cpython-38.pyc,, +flask/__pycache__/helpers.cpython-38.pyc,, +flask/__pycache__/logging.cpython-38.pyc,, +flask/__pycache__/scaffold.cpython-38.pyc,, +flask/__pycache__/sessions.cpython-38.pyc,, +flask/__pycache__/signals.cpython-38.pyc,, +flask/__pycache__/templating.cpython-38.pyc,, +flask/__pycache__/testing.cpython-38.pyc,, +flask/__pycache__/typing.cpython-38.pyc,, +flask/__pycache__/views.cpython-38.pyc,, +flask/__pycache__/wrappers.cpython-38.pyc,, +flask/app.py,sha256=ectBbi9hGmVHAse5TNcFQZIDRkDAxYUAnLgfuKD0Xws,81975 +flask/blueprints.py,sha256=AkAVXZ_MMkjwjklzCAMdBNowTiM0wVQPynnUnXjTL2M,23781 +flask/cli.py,sha256=wn2Un9RO32ZfRmCMem5KJ5h62-5lnmy1H9uxgyV-eBs,32238 +flask/config.py,sha256=70Uyjh1Jzb9MfTCT7NDhuZWAzyIEu-TIyk6-22MP3zQ,11285 +flask/ctx.py,sha256=EM3W0v1ctuFQAGk_HWtQdoJEg_r2f5Le4xcmElxFwwk,17428 +flask/debughelpers.py,sha256=W82-xrRmodjopBngI9roYH-q08EbQwN2HEGfDAi6SA0,6184 +flask/globals.py,sha256=cWd-R2hUH3VqPhnmQNww892tQS6Yjqg_wg8UvW1M7NM,1723 +flask/helpers.py,sha256=00WqA3wYeyjMrnAOPZTUyrnUf7H8ik3CVT0kqGl_qjk,30589 +flask/json/__init__.py,sha256=unAKdZBlxMI5OMiTU0-Z2Hl4CF1CMJmqTUzpStiExNw,11822 +flask/json/__pycache__/__init__.cpython-38.pyc,, +flask/json/__pycache__/tag.cpython-38.pyc,, +flask/json/tag.py,sha256=fys3HBLssWHuMAIJuTcf2K0bCtosePBKXIWASZEEjnU,8857 +flask/logging.py,sha256=1o_hirVGqdj7SBdETnhX7IAjklG89RXlrwz_2CjzQQE,2273 +flask/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +flask/scaffold.py,sha256=fM9mRy7QBh9fhJ0VTogVx900dDa5oxz8FOw6OK5F-TU,32796 +flask/sessions.py,sha256=Kb7zY4qBIOU2cw1xM5mQ_KmgYUBDFbUYWjlkq0EFYis,15189 +flask/signals.py,sha256=H7QwDciK-dtBxinjKpexpglP0E6k0MJILiFWTItfmqU,2136 +flask/templating.py,sha256=l96VD39JQ0nue4Bcj7wZ4-FWWs-ppLxvgBCpwDQ4KAk,5626 +flask/testing.py,sha256=OsHT-2B70abWH3ulY9IbhLchXIeyj3L-cfcDa88wv5E,10281 +flask/typing.py,sha256=hXEVcXoH-QEabmy1F11pYaQ2SonlkMAwfjBAnqj2x18,1982 +flask/views.py,sha256=nhq31TRB5Z-z2mjFGZACaaB2Et5XPCmWhWxJxOvLWww,5948 +flask/wrappers.py,sha256=VndbHPRBSUUOejmd2Y3ydkoCVUtsS2OJIdJEVIkBVD8,5604 diff --git a/flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/WHEEL b/flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/WHEEL new file mode 100644 index 000000000..5bad85fdc --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/entry_points.txt b/flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/entry_points.txt new file mode 100644 index 000000000..1eb025200 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] +flask = flask.cli:main + diff --git a/flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/top_level.txt b/flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/top_level.txt new file mode 100644 index 000000000..7e1060246 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Flask-2.0.2.dist-info/top_level.txt @@ -0,0 +1 @@ +flask diff --git a/flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/INSTALLER b/flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/LICENSE.rst b/flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/LICENSE.rst new file mode 100644 index 000000000..9d227a0cc --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2010 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/METADATA b/flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/METADATA new file mode 100644 index 000000000..a1971dc2d --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/METADATA @@ -0,0 +1,94 @@ +Metadata-Version: 2.1 +Name: Flask-SQLAlchemy +Version: 2.5.1 +Summary: Adds SQLAlchemy support to your Flask application. +Home-page: https://github.com/pallets/flask-sqlalchemy +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Documentation, https://flask-sqlalchemy.palletsprojects.com/ +Project-URL: Code, https://github.com/pallets/flask-sqlalchemy +Project-URL: Issue tracker, https://github.com/pallets/flask-sqlalchemy/issues +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD 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.4 +Classifier: Programming Language :: Python :: 3.5 +Classifier: Programming Language :: Python :: 3.6 +Classifier: Programming Language :: Python :: 3.7 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >= 2.7, != 3.0.*, != 3.1.*, != 3.2.*, != 3.3.* +Requires-Dist: Flask (>=0.10) +Requires-Dist: SQLAlchemy (>=0.8.0) + +Flask-SQLAlchemy +================ + +Flask-SQLAlchemy is an extension for `Flask`_ that adds support for +`SQLAlchemy`_ to your application. It aims to simplify using SQLAlchemy +with Flask by providing useful defaults and extra helpers that make it +easier to accomplish common tasks. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U Flask-SQLAlchemy + + +A Simple Example +---------------- + +.. code-block:: python + + from flask import Flask + from flask_sqlalchemy import SQLAlchemy + + app = Flask(__name__) + app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///example.sqlite" + db = SQLAlchemy(app) + + + class User(db.Model): + id = db.Column(db.Integer, primary_key=True) + username = db.Column(db.String, unique=True, nullable=False) + email = db.Column(db.String, unique=True, nullable=False) + + + db.session.add(User(name="Flask", email="example@example.com")) + db.session.commit() + + users = User.query.all() + + +Links +----- + +- Documentation: https://flask-sqlalchemy.palletsprojects.com/ +- Releases: https://pypi.org/project/Flask-SQLAlchemy/ +- Code: https://github.com/pallets/flask-sqlalchemy +- Issue tracker: https://github.com/pallets/flask-sqlalchemy/issues +- Test status: https://travis-ci.org/pallets/flask-sqlalchemy +- Test coverage: https://codecov.io/gh/pallets/flask-sqlalchemy + +.. _Flask: https://palletsprojects.com/p/flask/ +.. _SQLAlchemy: https://www.sqlalchemy.org +.. _pip: https://pip.pypa.io/en/stable/quickstart/ + + diff --git a/flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/RECORD b/flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/RECORD new file mode 100644 index 000000000..69abba111 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/RECORD @@ -0,0 +1,14 @@ +Flask_SQLAlchemy-2.5.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Flask_SQLAlchemy-2.5.1.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +Flask_SQLAlchemy-2.5.1.dist-info/METADATA,sha256=vVCeMtTM_xOrUVoVyemeNaTUI5L9iXa16NsiMDDOgFU,3128 +Flask_SQLAlchemy-2.5.1.dist-info/RECORD,, +Flask_SQLAlchemy-2.5.1.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110 +Flask_SQLAlchemy-2.5.1.dist-info/top_level.txt,sha256=w2K4fNNoTh4HItoFfz2FRQShSeLcvHYrzU_sZov21QU,17 +flask_sqlalchemy/__init__.py,sha256=IaupgTRkQnY05KPLYvfiNnJdrmwoyfsxaiyGtrEYfO4,40738 +flask_sqlalchemy/__pycache__/__init__.cpython-38.pyc,, +flask_sqlalchemy/__pycache__/_compat.cpython-38.pyc,, +flask_sqlalchemy/__pycache__/model.cpython-38.pyc,, +flask_sqlalchemy/__pycache__/utils.cpython-38.pyc,, +flask_sqlalchemy/_compat.py,sha256=yua0ZSgVWwi56QpEgwaPInzkNQ9PFb7YQdvEk3dImXo,821 +flask_sqlalchemy/model.py,sha256=bd2mIv9LA1A2MZkQObgnMUCSrxNvyqplaSkCxyxKNxY,4988 +flask_sqlalchemy/utils.py,sha256=4eHqAbYElnJ3NbSAHhuINckoAHDABoxjleMJD0iKgyg,1390 diff --git a/flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/WHEEL b/flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/WHEEL new file mode 100644 index 000000000..01b8fc7d4 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/top_level.txt b/flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/top_level.txt new file mode 100644 index 000000000..8a5538e94 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Flask_SQLAlchemy-2.5.1.dist-info/top_level.txt @@ -0,0 +1 @@ +flask_sqlalchemy diff --git a/flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/INSTALLER b/flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/LICENSE.rst b/flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/LICENSE.rst new file mode 100644 index 000000000..c37cae49e --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2007 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/METADATA b/flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/METADATA new file mode 100644 index 000000000..3b9355ac0 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/METADATA @@ -0,0 +1,113 @@ +Metadata-Version: 2.1 +Name: Jinja2 +Version: 3.0.3 +Summary: A very fast and expressive template engine. +Home-page: https://palletsprojects.com/p/jinja/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://jinja.palletsprojects.com/ +Project-URL: Changes, https://jinja.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/jinja/ +Project-URL: Issue Tracker, https://github.com/pallets/jinja/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: MarkupSafe (>=2.0) +Provides-Extra: i18n +Requires-Dist: Babel (>=2.7) ; extra == 'i18n' + +Jinja +===== + +Jinja is a fast, expressive, extensible templating engine. Special +placeholders in the template allow writing code similar to Python +syntax. Then the template is passed data to render the final document. + +It includes: + +- Template inheritance and inclusion. +- Define and import macros within templates. +- HTML templates can use autoescaping to prevent XSS from untrusted + user input. +- A sandboxed environment can safely render untrusted templates. +- AsyncIO support for generating templates and calling async + functions. +- I18N support with Babel. +- Templates are compiled to optimized Python code just-in-time and + cached, or can be compiled ahead-of-time. +- Exceptions point to the correct line in templates to make debugging + easier. +- Extensible filters, tests, functions, and even syntax. + +Jinja's philosophy is that while application logic belongs in Python if +possible, it shouldn't make the template designer's job difficult by +restricting functionality too much. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U Jinja2 + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +In A Nutshell +------------- + +.. code-block:: jinja + + {% extends "base.html" %} + {% block title %}Members{% endblock %} + {% block content %} + + {% endblock %} + + +Donate +------ + +The Pallets organization develops and supports Jinja and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, `please +donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://jinja.palletsprojects.com/ +- Changes: https://jinja.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/Jinja2/ +- Source Code: https://github.com/pallets/jinja/ +- Issue Tracker: https://github.com/pallets/jinja/issues/ +- Website: https://palletsprojects.com/p/jinja/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/RECORD b/flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/RECORD new file mode 100644 index 000000000..e60c78b8f --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/RECORD @@ -0,0 +1,58 @@ +Jinja2-3.0.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Jinja2-3.0.3.dist-info/LICENSE.rst,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +Jinja2-3.0.3.dist-info/METADATA,sha256=uvKoBSMLvh0qHK-6khEqSe1yOV4jxFzbPSREOp-3BXk,3539 +Jinja2-3.0.3.dist-info/RECORD,, +Jinja2-3.0.3.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 +Jinja2-3.0.3.dist-info/entry_points.txt,sha256=Qy_DkVo6Xj_zzOtmErrATe8lHZhOqdjpt3e4JJAGyi8,61 +Jinja2-3.0.3.dist-info/top_level.txt,sha256=PkeVWtLb3-CqjWi1fO29OCbj55EhX_chhKrCdrVe_zs,7 +jinja2/__init__.py,sha256=V3JjnTV-nyIHN6rwj03N1M11fegjGvv-weiHMQwH1pk,2205 +jinja2/__pycache__/__init__.cpython-38.pyc,, +jinja2/__pycache__/_identifier.cpython-38.pyc,, +jinja2/__pycache__/async_utils.cpython-38.pyc,, +jinja2/__pycache__/bccache.cpython-38.pyc,, +jinja2/__pycache__/compiler.cpython-38.pyc,, +jinja2/__pycache__/constants.cpython-38.pyc,, +jinja2/__pycache__/debug.cpython-38.pyc,, +jinja2/__pycache__/defaults.cpython-38.pyc,, +jinja2/__pycache__/environment.cpython-38.pyc,, +jinja2/__pycache__/exceptions.cpython-38.pyc,, +jinja2/__pycache__/ext.cpython-38.pyc,, +jinja2/__pycache__/filters.cpython-38.pyc,, +jinja2/__pycache__/idtracking.cpython-38.pyc,, +jinja2/__pycache__/lexer.cpython-38.pyc,, +jinja2/__pycache__/loaders.cpython-38.pyc,, +jinja2/__pycache__/meta.cpython-38.pyc,, +jinja2/__pycache__/nativetypes.cpython-38.pyc,, +jinja2/__pycache__/nodes.cpython-38.pyc,, +jinja2/__pycache__/optimizer.cpython-38.pyc,, +jinja2/__pycache__/parser.cpython-38.pyc,, +jinja2/__pycache__/runtime.cpython-38.pyc,, +jinja2/__pycache__/sandbox.cpython-38.pyc,, +jinja2/__pycache__/tests.cpython-38.pyc,, +jinja2/__pycache__/utils.cpython-38.pyc,, +jinja2/__pycache__/visitor.cpython-38.pyc,, +jinja2/_identifier.py,sha256=EdgGJKi7O1yvr4yFlvqPNEqV6M1qHyQr8Gt8GmVTKVM,1775 +jinja2/async_utils.py,sha256=jBcJSmLoQa2PjJdNcOpwaUmBxFNE9rZNwMF7Ob3dP9I,1947 +jinja2/bccache.py,sha256=v5rKAlYxIvfJEa0uGzAC6yCYSS3KuXT5Eqi-n9qvNi8,12670 +jinja2/compiler.py,sha256=v7zKz-mgSYXmfXD9mRmi2BU0B6Z-1RGZmOXCrsPKzc0,72209 +jinja2/constants.py,sha256=GMoFydBF_kdpaRKPoM5cl5MviquVRLVyZtfp5-16jg0,1433 +jinja2/debug.py,sha256=r0JL0vfO7HPlyKZEdr6eVlg7HoIg2OQGmJ7SeUEyAeI,8494 +jinja2/defaults.py,sha256=boBcSw78h-lp20YbaXSJsqkAI2uN_mD_TtCydpeq5wU,1267 +jinja2/environment.py,sha256=Vz20npBX5-SUH_eguQuxrSQDEsLFjho0qcHLdMhY3hA,60983 +jinja2/exceptions.py,sha256=ioHeHrWwCWNaXX1inHmHVblvc4haO7AXsjCp3GfWvx0,5071 +jinja2/ext.py,sha256=44SjDjeYkkxQTpmC2BetOTxEFMgQ42p2dfSwXmPFcSo,32122 +jinja2/filters.py,sha256=jusKTZbd0ddZMaibZkxMUVKNsOsaYtOq_Il8Imtx4BE,52609 +jinja2/idtracking.py,sha256=WekexMql3u5n3vDxFsQ_i8HW0j24AtjWTjrPBLWrHww,10721 +jinja2/lexer.py,sha256=qNEQqDQw_zO5EaH6rFQsER7Qwn2du0o22prB-TR11HE,29930 +jinja2/loaders.py,sha256=1MjXJOU6p4VywFqtpDZhtvtT_vIlmHnZKMKHHw4SZzA,22754 +jinja2/meta.py,sha256=GNPEvifmSaU3CMxlbheBOZjeZ277HThOPUTf1RkppKQ,4396 +jinja2/nativetypes.py,sha256=KCJl71MogrDih_BHBu6xV5p7Cr_jggAgu-shKTg6L28,3969 +jinja2/nodes.py,sha256=i34GPRAZexXMT6bwuf5SEyvdmS-bRCy9KMjwN5O6pjk,34550 +jinja2/optimizer.py,sha256=tHkMwXxfZkbfA1KmLcqmBMSaz7RLIvvItrJcPoXTyD8,1650 +jinja2/parser.py,sha256=kHnU8v92GwMYkfr0MVakWv8UlSf_kJPx8LUsgQMof70,39767 +jinja2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +jinja2/runtime.py,sha256=wVRlkEmAgNU67AIQDqLvI6UkNLkzDqpLA-z4Mi3vl3g,35054 +jinja2/sandbox.py,sha256=-8zxR6TO9kUkciAVFsIKu8Oq-C7PTeYEdZ5TtA55-gw,14600 +jinja2/tests.py,sha256=Am5Z6Lmfr2XaH_npIfJJ8MdXtWsbLjMULZJulTAj30E,5905 +jinja2/utils.py,sha256=udQxWIKaq4QDCZiXN31ngKOaGGdaMA5fl0JMaM-F6fg,26971 +jinja2/visitor.py,sha256=ZmeLuTj66ic35-uFH-1m0EKXiw4ObDDb_WuE6h5vPFg,3572 diff --git a/flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/WHEEL b/flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/WHEEL new file mode 100644 index 000000000..5bad85fdc --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/entry_points.txt b/flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/entry_points.txt new file mode 100644 index 000000000..3619483fd --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/entry_points.txt @@ -0,0 +1,3 @@ +[babel.extractors] +jinja2 = jinja2.ext:babel_extract [i18n] + diff --git a/flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/top_level.txt b/flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/top_level.txt new file mode 100644 index 000000000..7f7afbf3b --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Jinja2-3.0.3.dist-info/top_level.txt @@ -0,0 +1 @@ +jinja2 diff --git a/flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/INSTALLER b/flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/LICENSE.rst b/flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/LICENSE.rst new file mode 100644 index 000000000..9d227a0cc --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2010 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/METADATA b/flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/METADATA new file mode 100644 index 000000000..ef44e2b35 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/METADATA @@ -0,0 +1,100 @@ +Metadata-Version: 2.1 +Name: MarkupSafe +Version: 2.0.1 +Summary: Safely add untrusted strings to HTML/XML markup. +Home-page: https://palletsprojects.com/p/markupsafe/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://markupsafe.palletsprojects.com/ +Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/markupsafe/ +Project-URL: Issue Tracker, https://github.com/pallets/markupsafe/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Text Processing :: Markup :: HTML +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst + +MarkupSafe +========== + +MarkupSafe implements a text object that escapes characters so it is +safe to use in HTML and XML. Characters that have special meanings are +replaced so that they display as the actual characters. This mitigates +injection attacks, meaning untrusted user input can safely be displayed +on a page. + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + pip install -U MarkupSafe + +.. _pip: https://pip.pypa.io/en/stable/quickstart/ + + +Examples +-------- + +.. code-block:: pycon + + >>> from markupsafe import Markup, escape + + >>> # escape replaces special characters and wraps in Markup + >>> escape("") + Markup('<script>alert(document.cookie);</script>') + + >>> # wrap in Markup to mark text "safe" and prevent escaping + >>> Markup("Hello") + Markup('hello') + + >>> escape(Markup("Hello")) + Markup('hello') + + >>> # Markup is a str subclass + >>> # methods and operators escape their arguments + >>> template = Markup("Hello {name}") + >>> template.format(name='"World"') + Markup('Hello "World"') + + +Donate +------ + +The Pallets organization develops and supports MarkupSafe and other +popular packages. In order to grow the community of contributors and +users, and allow the maintainers to devote more time to the projects, +`please donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://markupsafe.palletsprojects.com/ +- Changes: https://markupsafe.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/MarkupSafe/ +- Source Code: https://github.com/pallets/markupsafe/ +- Issue Tracker: https://github.com/pallets/markupsafe/issues/ +- Website: https://palletsprojects.com/p/markupsafe/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/RECORD b/flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/RECORD new file mode 100644 index 000000000..274f15114 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/RECORD @@ -0,0 +1,14 @@ +MarkupSafe-2.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +MarkupSafe-2.0.1.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475 +MarkupSafe-2.0.1.dist-info/METADATA,sha256=FmPpxBdaqCCjF-XKqoxeEzqAzhetQnrkkSsd3V3X-Jc,3211 +MarkupSafe-2.0.1.dist-info/RECORD,, +MarkupSafe-2.0.1.dist-info/WHEEL,sha256=RA4ju32EWHpO-G1BhxTQ3AwXQWjnnrzYGkM9skoN7_I,109 +MarkupSafe-2.0.1.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11 +markupsafe/__init__.py,sha256=9Tez4UIlI7J6_sQcUFK1dKniT_b_8YefpGIyYJ3Sr2Q,8923 +markupsafe/__pycache__/__init__.cpython-38.pyc,, +markupsafe/__pycache__/_native.cpython-38.pyc,, +markupsafe/_native.py,sha256=GTKEV-bWgZuSjklhMHOYRHU9k0DMewTf5mVEZfkbuns,1986 +markupsafe/_speedups.c,sha256=CDDtwaV21D2nYtypnMQzxvvpZpcTvIs8OZ6KDa1g4t0,7400 +markupsafe/_speedups.cpython-38-darwin.so,sha256=Tjek-7TDXevv7qgNBsLLUfsOQlfCSCGzhhV5cCUrIxk,35480 +markupsafe/_speedups.pyi,sha256=vfMCsOgbAXRNLUXkyuyonG8uEWKYU4PDqNuMaDELAYw,229 +markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 diff --git a/flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/WHEEL b/flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/WHEEL new file mode 100644 index 000000000..d70ba8e3f --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: false +Tag: cp38-cp38-macosx_10_9_x86_64 + diff --git a/flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/top_level.txt b/flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/top_level.txt new file mode 100644 index 000000000..75bf72925 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/MarkupSafe-2.0.1.dist-info/top_level.txt @@ -0,0 +1 @@ +markupsafe diff --git a/flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/INSTALLER b/flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/LICENSE b/flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/LICENSE new file mode 100644 index 000000000..0d9fb6dc4 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/LICENSE @@ -0,0 +1,19 @@ +Copyright 2005-2021 SQLAlchemy authors and contributors . + +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. diff --git a/flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/METADATA b/flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/METADATA new file mode 100644 index 000000000..cc9841916 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/METADATA @@ -0,0 +1,239 @@ +Metadata-Version: 2.1 +Name: SQLAlchemy +Version: 1.4.27 +Summary: Database Abstraction Library +Home-page: https://www.sqlalchemy.org +Author: Mike Bayer +Author-email: mike_mp@zzzcomputing.com +License: MIT +Project-URL: Documentation, https://docs.sqlalchemy.org +Project-URL: Issue Tracker, https://github.com/sqlalchemy/sqlalchemy/ +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.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: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Database :: Front-Ends +Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7 +Description-Content-Type: text/x-rst +Requires-Dist: importlib-metadata ; python_version < "3.8" +Requires-Dist: greenlet (!=0.4.17) ; python_version >= "3" and (platform_machine == "aarch64" or (platform_machine == "ppc64le" or (platform_machine == "x86_64" or (platform_machine == "amd64" or (platform_machine == "AMD64" or (platform_machine == "win32" or platform_machine == "WIN32")))))) +Provides-Extra: aiomysql +Requires-Dist: greenlet (!=0.4.17) ; (python_version >= "3") and extra == 'aiomysql' +Requires-Dist: aiomysql ; (python_version >= "3") and extra == 'aiomysql' +Provides-Extra: aiosqlite +Requires-Dist: typing-extensions (!=3.10.0.1) ; extra == 'aiosqlite' +Requires-Dist: greenlet (!=0.4.17) ; (python_version >= "3") and extra == 'aiosqlite' +Requires-Dist: aiosqlite ; (python_version >= "3") and extra == 'aiosqlite' +Provides-Extra: asyncio +Requires-Dist: greenlet (!=0.4.17) ; (python_version >= "3") and extra == 'asyncio' +Provides-Extra: asyncmy +Requires-Dist: greenlet (!=0.4.17) ; (python_version >= "3") and extra == 'asyncmy' +Requires-Dist: asyncmy (>=0.2.3) ; (python_version >= "3") and extra == 'asyncmy' +Provides-Extra: mariadb_connector +Requires-Dist: mariadb (>=1.0.1) ; (python_version >= "3") and extra == 'mariadb_connector' +Provides-Extra: mssql +Requires-Dist: pyodbc ; extra == 'mssql' +Provides-Extra: mssql_pymssql +Requires-Dist: pymssql ; extra == 'mssql_pymssql' +Provides-Extra: mssql_pyodbc +Requires-Dist: pyodbc ; extra == 'mssql_pyodbc' +Provides-Extra: mypy +Requires-Dist: sqlalchemy2-stubs ; extra == 'mypy' +Requires-Dist: mypy (>=0.910) ; (python_version >= "3") and extra == 'mypy' +Provides-Extra: mysql +Requires-Dist: mysqlclient (<2,>=1.4.0) ; (python_version < "3") and extra == 'mysql' +Requires-Dist: mysqlclient (>=1.4.0) ; (python_version >= "3") and extra == 'mysql' +Provides-Extra: mysql_connector +Requires-Dist: mysql-connector-python ; extra == 'mysql_connector' +Provides-Extra: oracle +Requires-Dist: cx-oracle (<8,>=7) ; (python_version < "3") and extra == 'oracle' +Requires-Dist: cx-oracle (>=7) ; (python_version >= "3") and extra == 'oracle' +Provides-Extra: postgresql +Requires-Dist: psycopg2 (>=2.7) ; extra == 'postgresql' +Provides-Extra: postgresql_asyncpg +Requires-Dist: greenlet (!=0.4.17) ; (python_version >= "3") and extra == 'postgresql_asyncpg' +Requires-Dist: asyncpg ; (python_version >= "3") and extra == 'postgresql_asyncpg' +Provides-Extra: postgresql_pg8000 +Requires-Dist: pg8000 (>=1.16.6) ; extra == 'postgresql_pg8000' +Provides-Extra: postgresql_psycopg2binary +Requires-Dist: psycopg2-binary ; extra == 'postgresql_psycopg2binary' +Provides-Extra: postgresql_psycopg2cffi +Requires-Dist: psycopg2cffi ; extra == 'postgresql_psycopg2cffi' +Provides-Extra: pymysql +Requires-Dist: pymysql (<1) ; (python_version < "3") and extra == 'pymysql' +Requires-Dist: pymysql ; (python_version >= "3") and extra == 'pymysql' +Provides-Extra: sqlcipher +Requires-Dist: sqlcipher3-binary ; (python_version >= "3") and extra == 'sqlcipher' + +SQLAlchemy +========== + +|PyPI| |Python| |Downloads| + +.. |PyPI| image:: https://img.shields.io/pypi/v/sqlalchemy + :target: https://pypi.org/project/sqlalchemy + :alt: PyPI + +.. |Python| image:: https://img.shields.io/pypi/pyversions/sqlalchemy + :target: https://pypi.org/project/sqlalchemy + :alt: PyPI - Python Version + +.. |Downloads| image:: https://img.shields.io/pypi/dm/sqlalchemy + :target: https://pypi.org/project/sqlalchemy + :alt: PyPI - Downloads + + +The Python SQL Toolkit and Object Relational Mapper + +Introduction +------------- + +SQLAlchemy is the Python SQL toolkit and Object Relational Mapper +that gives application developers the full power and +flexibility of SQL. SQLAlchemy provides a full suite +of well known enterprise-level persistence patterns, +designed for efficient and high-performing database +access, adapted into a simple and Pythonic domain +language. + +Major SQLAlchemy features include: + +* An industrial strength ORM, built + from the core on the identity map, unit of work, + and data mapper patterns. These patterns + allow transparent persistence of objects + using a declarative configuration system. + Domain models + can be constructed and manipulated naturally, + and changes are synchronized with the + current transaction automatically. +* A relationally-oriented query system, exposing + the full range of SQL's capabilities + explicitly, including joins, subqueries, + correlation, and most everything else, + in terms of the object model. + Writing queries with the ORM uses the same + techniques of relational composition you use + when writing SQL. While you can drop into + literal SQL at any time, it's virtually never + needed. +* A comprehensive and flexible system + of eager loading for related collections and objects. + Collections are cached within a session, + and can be loaded on individual access, all + at once using joins, or by query per collection + across the full result set. +* A Core SQL construction system and DBAPI + interaction layer. The SQLAlchemy Core is + separate from the ORM and is a full database + abstraction layer in its own right, and includes + an extensible Python-based SQL expression + language, schema metadata, connection pooling, + type coercion, and custom types. +* All primary and foreign key constraints are + assumed to be composite and natural. Surrogate + integer primary keys are of course still the + norm, but SQLAlchemy never assumes or hardcodes + to this model. +* Database introspection and generation. Database + schemas can be "reflected" in one step into + Python structures representing database metadata; + those same structures can then generate + CREATE statements right back out - all within + the Core, independent of the ORM. + +SQLAlchemy's philosophy: + +* SQL databases behave less and less like object + collections the more size and performance start to + matter; object collections behave less and less like + tables and rows the more abstraction starts to matter. + SQLAlchemy aims to accommodate both of these + principles. +* An ORM doesn't need to hide the "R". A relational + database provides rich, set-based functionality + that should be fully exposed. SQLAlchemy's + ORM provides an open-ended set of patterns + that allow a developer to construct a custom + mediation layer between a domain model and + a relational schema, turning the so-called + "object relational impedance" issue into + a distant memory. +* The developer, in all cases, makes all decisions + regarding the design, structure, and naming conventions + of both the object model as well as the relational + schema. SQLAlchemy only provides the means + to automate the execution of these decisions. +* With SQLAlchemy, there's no such thing as + "the ORM generated a bad query" - you + retain full control over the structure of + queries, including how joins are organized, + how subqueries and correlation is used, what + columns are requested. Everything SQLAlchemy + does is ultimately the result of a developer- + initiated decision. +* Don't use an ORM if the problem doesn't need one. + SQLAlchemy consists of a Core and separate ORM + component. The Core offers a full SQL expression + language that allows Pythonic construction + of SQL constructs that render directly to SQL + strings for a target database, returning + result sets that are essentially enhanced DBAPI + cursors. +* Transactions should be the norm. With SQLAlchemy's + ORM, nothing goes to permanent storage until + commit() is called. SQLAlchemy encourages applications + to create a consistent means of delineating + the start and end of a series of operations. +* Never render a literal value in a SQL statement. + Bound parameters are used to the greatest degree + possible, allowing query optimizers to cache + query plans effectively and making SQL injection + attacks a non-issue. + +Documentation +------------- + +Latest documentation is at: + +https://www.sqlalchemy.org/docs/ + +Installation / Requirements +--------------------------- + +Full documentation for installation is at +`Installation `_. + +Getting Help / Development / Bug reporting +------------------------------------------ + +Please refer to the `SQLAlchemy Community Guide `_. + +Code of Conduct +--------------- + +Above all, SQLAlchemy places great emphasis on polite, thoughtful, and +constructive communication between users and developers. +Please see our current Code of Conduct at +`Code of Conduct `_. + +License +------- + +SQLAlchemy is distributed under the `MIT license +`_. + + + diff --git a/flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/RECORD b/flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/RECORD new file mode 100644 index 000000000..3d89d46a1 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/RECORD @@ -0,0 +1,485 @@ +SQLAlchemy-1.4.27.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +SQLAlchemy-1.4.27.dist-info/LICENSE,sha256=_-DCK5JvsC0ovMsgocueJWTu1m_PSeTv7r8oHE-pf6c,1100 +SQLAlchemy-1.4.27.dist-info/METADATA,sha256=r_0ZuzMG-zLC1Xu6xgYd0saZ0yHW8gXPE_9XxjvII-A,9945 +SQLAlchemy-1.4.27.dist-info/RECORD,, +SQLAlchemy-1.4.27.dist-info/WHEEL,sha256=Hxr7O-X8T9h9kBRUEXrRY-j217oAickuD6_DXvSLvAU,110 +SQLAlchemy-1.4.27.dist-info/top_level.txt,sha256=rp-ZgB7D8G11ivXON5VGPjupT1voYmWqkciDt5Uaw_Q,11 +sqlalchemy/__init__.py,sha256=rKNVSFLbGkYhTRpfm7QFXZoij92dwPx-736en34wrqM,4114 +sqlalchemy/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/__pycache__/events.cpython-38.pyc,, +sqlalchemy/__pycache__/exc.cpython-38.pyc,, +sqlalchemy/__pycache__/inspection.cpython-38.pyc,, +sqlalchemy/__pycache__/log.cpython-38.pyc,, +sqlalchemy/__pycache__/processors.cpython-38.pyc,, +sqlalchemy/__pycache__/schema.cpython-38.pyc,, +sqlalchemy/__pycache__/types.cpython-38.pyc,, +sqlalchemy/cimmutabledict.cpython-38-darwin.so,sha256=zdATsu1CZoQvAheaGD3STSGWEcQcY6ReCinQDb3PDzw,37864 +sqlalchemy/connectors/__init__.py,sha256=O443ri6SrKVeRqNLMyqjX0DHFvuPxo9AdZIDkodhxwA,279 +sqlalchemy/connectors/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/connectors/__pycache__/mxodbc.cpython-38.pyc,, +sqlalchemy/connectors/__pycache__/pyodbc.cpython-38.pyc,, +sqlalchemy/connectors/mxodbc.py,sha256=EbSWZRvQFw2Ng0ec9MN4KtLJvOuTPw4lSYglho5rYL8,5784 +sqlalchemy/connectors/pyodbc.py,sha256=qVLG7itujednjC-rPVn7VWW07Mou7dDBJmNQdUhTXtk,6825 +sqlalchemy/cprocessors.cpython-38-darwin.so,sha256=z9-KZfQzsIUsW_qrayRkWNNIP7MSYA9wBgY-bLHmxuI,37272 +sqlalchemy/cresultproxy.cpython-38-darwin.so,sha256=YFfY6cpwnt_W_n7ZzD14jdtt97fHQI72AeAp7UQic3Q,41376 +sqlalchemy/databases/__init__.py,sha256=vGQM3BYXHXy6RBTFNiL80biiW3fn-LoguUjJKiFnStE,1010 +sqlalchemy/databases/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/dialects/__init__.py,sha256=66uS-lZx94aGVQvEqy_z8m1pC0P3cI-CKEWCIL2Xlsk,2085 +sqlalchemy/dialects/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/dialects/firebird/__init__.py,sha256=wZ9npV8FYElLZEYmrP1ksvN90_6YR1RkIHnT6rjxhfs,1153 +sqlalchemy/dialects/firebird/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/dialects/firebird/__pycache__/base.cpython-38.pyc,, +sqlalchemy/dialects/firebird/__pycache__/fdb.cpython-38.pyc,, +sqlalchemy/dialects/firebird/__pycache__/kinterbasdb.cpython-38.pyc,, +sqlalchemy/dialects/firebird/base.py,sha256=wUBiQwvIf35OdNUfU_Vi_rtGYeIdM7DUKogfh0KYzRY,31171 +sqlalchemy/dialects/firebird/fdb.py,sha256=w4Kc-IubUKZgY5yTcgewvzZcU2WOnuwXM94dePqbEmk,4116 +sqlalchemy/dialects/firebird/kinterbasdb.py,sha256=dQbCC8vGifRyeQhekSc_t0Zj5XKFpTH0C2XyBcxRun0,6479 +sqlalchemy/dialects/mssql/__init__.py,sha256=-tzu6QvNJpSsrB_OqbqA1WnHGJdTiFe0LRRFEBL8qiY,1788 +sqlalchemy/dialects/mssql/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/dialects/mssql/__pycache__/base.cpython-38.pyc,, +sqlalchemy/dialects/mssql/__pycache__/information_schema.cpython-38.pyc,, +sqlalchemy/dialects/mssql/__pycache__/json.cpython-38.pyc,, +sqlalchemy/dialects/mssql/__pycache__/mxodbc.cpython-38.pyc,, +sqlalchemy/dialects/mssql/__pycache__/provision.cpython-38.pyc,, +sqlalchemy/dialects/mssql/__pycache__/pymssql.cpython-38.pyc,, +sqlalchemy/dialects/mssql/__pycache__/pyodbc.cpython-38.pyc,, +sqlalchemy/dialects/mssql/base.py,sha256=6Z7fEN1eCczHcI1xrlWHGV1Km3VFQIc-hdgi75IoU60,114380 +sqlalchemy/dialects/mssql/information_schema.py,sha256=MeIFJ0gK1Um0jE1S0rG5q3cv3Mk6N_PftPUR0h7F7qU,7584 +sqlalchemy/dialects/mssql/json.py,sha256=K1RqVl5bslYyVMtk5CWGjRV_I4K1sszXjx2F_nbCVWI,4558 +sqlalchemy/dialects/mssql/mxodbc.py,sha256=QHeIbRAlNxM47dNkTaly1Qvhjoc627YsF-iTKuL_goY,4808 +sqlalchemy/dialects/mssql/provision.py,sha256=m7ofLZYZinDS91Vgs42fK7dhJNnH-J_Bw2x_tP59tCc,4255 +sqlalchemy/dialects/mssql/pymssql.py,sha256=xBkFqpSSZ2cCn4Cop6NuV4ZBN_ARVyZzh_HKpkNIRLY,4843 +sqlalchemy/dialects/mssql/pyodbc.py,sha256=Rv-J3jEB9G6p7eNnzB0boSzYuU36QEyLeq9bmc4zhpQ,21965 +sqlalchemy/dialects/mysql/__init__.py,sha256=2yMggm7oNcGHDrwBSBd2x7JRaYaBXl8hRHZKjW3tnuQ,2190 +sqlalchemy/dialects/mysql/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/dialects/mysql/__pycache__/aiomysql.cpython-38.pyc,, +sqlalchemy/dialects/mysql/__pycache__/asyncmy.cpython-38.pyc,, +sqlalchemy/dialects/mysql/__pycache__/base.cpython-38.pyc,, +sqlalchemy/dialects/mysql/__pycache__/cymysql.cpython-38.pyc,, +sqlalchemy/dialects/mysql/__pycache__/dml.cpython-38.pyc,, +sqlalchemy/dialects/mysql/__pycache__/enumerated.cpython-38.pyc,, +sqlalchemy/dialects/mysql/__pycache__/expression.cpython-38.pyc,, +sqlalchemy/dialects/mysql/__pycache__/json.cpython-38.pyc,, +sqlalchemy/dialects/mysql/__pycache__/mariadb.cpython-38.pyc,, +sqlalchemy/dialects/mysql/__pycache__/mariadbconnector.cpython-38.pyc,, +sqlalchemy/dialects/mysql/__pycache__/mysqlconnector.cpython-38.pyc,, +sqlalchemy/dialects/mysql/__pycache__/mysqldb.cpython-38.pyc,, +sqlalchemy/dialects/mysql/__pycache__/oursql.cpython-38.pyc,, +sqlalchemy/dialects/mysql/__pycache__/provision.cpython-38.pyc,, +sqlalchemy/dialects/mysql/__pycache__/pymysql.cpython-38.pyc,, +sqlalchemy/dialects/mysql/__pycache__/pyodbc.cpython-38.pyc,, +sqlalchemy/dialects/mysql/__pycache__/reflection.cpython-38.pyc,, +sqlalchemy/dialects/mysql/__pycache__/reserved_words.cpython-38.pyc,, +sqlalchemy/dialects/mysql/__pycache__/types.cpython-38.pyc,, +sqlalchemy/dialects/mysql/aiomysql.py,sha256=JO7VVQneB93f48DIfsu63PJW-OHaQ3m3Wd-4YPpdSSE,9609 +sqlalchemy/dialects/mysql/asyncmy.py,sha256=4tiiRm3l0ncgGwf5WzVik6z-IPD-ZcLEEJx4swMy_N0,9906 +sqlalchemy/dialects/mysql/base.py,sha256=mVnY2O7onyIRRoJEeeobNRVQclAx5ok3gl_xYO3SG5A,114150 +sqlalchemy/dialects/mysql/cymysql.py,sha256=MN5fsHApPDQxDHRPWHqSm6vznMWgxCJOtg4vEHuaMYs,2271 +sqlalchemy/dialects/mysql/dml.py,sha256=rCXGbiKl8iMi7AS30_turHWeouusLGSpSLtdHKnoUl4,6182 +sqlalchemy/dialects/mysql/enumerated.py,sha256=ZIy-3XQZtERuYv3jhvlnz5S_DCf-oiHACdgf2ymwEm4,9143 +sqlalchemy/dialects/mysql/expression.py,sha256=HJ4IO3LPJk4cQYIL-O-jN2vLWxVGCqem_K3h8kKNWzE,3741 +sqlalchemy/dialects/mysql/json.py,sha256=JWBHb0QmE9w47gsqZyfmUQpdi8GePHutGVJQVvixigg,2313 +sqlalchemy/dialects/mysql/mariadb.py,sha256=OBwN9RMQLP-xqLbNMAe5uoz7PEtqa68ln2HwwA6KUn8,585 +sqlalchemy/dialects/mysql/mariadbconnector.py,sha256=7FUPec_smSEl9omC_iQJx7Ywmz7Q-J_EFgfFklzNBz8,7337 +sqlalchemy/dialects/mysql/mysqlconnector.py,sha256=W8CL7P7hnZhmN3Pl6nwsOhwSMYvHWfCrlrWtx2F3zpU,7690 +sqlalchemy/dialects/mysql/mysqldb.py,sha256=k_mWzeIhlPK_Tcear93uKgoAZOX9Qfna7ls3u2KkSiw,10437 +sqlalchemy/dialects/mysql/oursql.py,sha256=m0lhnKgGli4u_DZdsFwgz0d2iXklB4rHfacj_QfM2Tw,8523 +sqlalchemy/dialects/mysql/provision.py,sha256=P5ma4Xy5eSOFIcMjIe_zAwu_6ncSXSLVZYYSMS5Io9c,2649 +sqlalchemy/dialects/mysql/pymysql.py,sha256=n9bgdO54bO1Dp8xS61PMpKoo1RUkPngwrlxBLX0Ovts,2770 +sqlalchemy/dialects/mysql/pyodbc.py,sha256=6XXmo7LluP1IfVe3dznOiC3wSH76q-tBpCSf2L9mS7w,4498 +sqlalchemy/dialects/mysql/reflection.py,sha256=eQpTm4N5swlAginvFhbOEiuVHFVdxcPAbAwMkzcS4n8,18553 +sqlalchemy/dialects/mysql/reserved_words.py,sha256=p9tEfMmf2qwB5mkkXQjARdtO9WoB4YYWpjYmHLXYVd0,9104 +sqlalchemy/dialects/mysql/types.py,sha256=x4SwOFKDmXmZbq88b6-dcHgo4CJzl8Np_gOsV8-0QZQ,24589 +sqlalchemy/dialects/oracle/__init__.py,sha256=a2cdiwS50KoRc0-3PrfFeTHwaw-aTb3NzGB0E60TmG8,1229 +sqlalchemy/dialects/oracle/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/dialects/oracle/__pycache__/base.cpython-38.pyc,, +sqlalchemy/dialects/oracle/__pycache__/cx_oracle.cpython-38.pyc,, +sqlalchemy/dialects/oracle/__pycache__/provision.cpython-38.pyc,, +sqlalchemy/dialects/oracle/base.py,sha256=Qy_X7KNsj5pNHEsb_daViUOxDqoTYaTW59X2NRFaRU0,86866 +sqlalchemy/dialects/oracle/cx_oracle.py,sha256=24c3oUQCylpuerqWdHEzvRq3MyIclKcAiukiUTUPM9w,52734 +sqlalchemy/dialects/oracle/provision.py,sha256=enaF61XI53b92R5LBUt1CPOLUMBWI7Ulktiqs7z54Yg,5805 +sqlalchemy/dialects/postgresql/__init__.py,sha256=mpE2L4a0CMcnYHcMBGpZfm3fTpzmTMqa3NHTtQtdGTE,2509 +sqlalchemy/dialects/postgresql/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/array.cpython-38.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/asyncpg.cpython-38.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/base.cpython-38.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/dml.cpython-38.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/ext.cpython-38.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/hstore.cpython-38.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/json.cpython-38.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/pg8000.cpython-38.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/provision.cpython-38.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/psycopg2.cpython-38.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/psycopg2cffi.cpython-38.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/pygresql.cpython-38.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/pypostgresql.cpython-38.pyc,, +sqlalchemy/dialects/postgresql/__pycache__/ranges.cpython-38.pyc,, +sqlalchemy/dialects/postgresql/array.py,sha256=AiOblctZe0Xu4nZP7kjqQWRkDjj0vuLmBmv-X-Pc3yg,12233 +sqlalchemy/dialects/postgresql/asyncpg.py,sha256=_YoJRX2llfKD2_hgXhDOSwtn4bAdB__U3gqHZFa_Jr8,34265 +sqlalchemy/dialects/postgresql/base.py,sha256=y--qcxDAR9PtV4bMxGKwWPZvFFxIyqGLwTpx1GtnUMs,153874 +sqlalchemy/dialects/postgresql/dml.py,sha256=6fsyXbbISrWCNq4tLs-hcLpXObtv7-xcWmuNR7pSUy8,9556 +sqlalchemy/dialects/postgresql/ext.py,sha256=pcX7pfWbzIoPOFLhXFqUrSYHXVbYAAoFZ9VLg-mE4aQ,8383 +sqlalchemy/dialects/postgresql/hstore.py,sha256=mh3VhuJa8_utkAXr-ZQNMZC9c-WK8SPD43M-2OJCudE,12496 +sqlalchemy/dialects/postgresql/json.py,sha256=bK-uBXv8r4ewZqUSoF-5JGzA2JcLAwJV6vkJDZ9OTMU,10556 +sqlalchemy/dialects/postgresql/pg8000.py,sha256=QhJevnFnjmK0JUu_UdeCfNjS2OTojX48pOfmrumr5JU,17044 +sqlalchemy/dialects/postgresql/provision.py,sha256=2hQBww2CBUz47MafjSVCVeCMoRWcCEG9fiTsKCm5KHk,4416 +sqlalchemy/dialects/postgresql/psycopg2.py,sha256=H4zoPsOjb5G76HZXU_eJyuU4QGeM8MzUWGLS7UAIjRA,38308 +sqlalchemy/dialects/postgresql/psycopg2cffi.py,sha256=lls7ZpikR4KvOQk2Nbh8z5IAT2Zu1D1Y280Mq4WQpOY,1691 +sqlalchemy/dialects/postgresql/pygresql.py,sha256=1qfzOFvEUExkvAiiFkLnVVHg6vfXGLzyp41UzBwKf24,8585 +sqlalchemy/dialects/postgresql/pypostgresql.py,sha256=3xY2pwLeYOBR7BCpj2pTtGtwcwS0VDDxM2GFmFogNPU,3693 +sqlalchemy/dialects/postgresql/ranges.py,sha256=Bfa9dLkWM51P-W_oAcnvbpUzsMg4ZrBWTN3ppi-n3Yc,4731 +sqlalchemy/dialects/sqlite/__init__.py,sha256=YQ5ryj0ZDE_3s559hsnm2cxuX2mI3ebEJ9Mf-DLmMA8,1198 +sqlalchemy/dialects/sqlite/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/aiosqlite.cpython-38.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/base.cpython-38.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/dml.cpython-38.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/json.cpython-38.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/provision.cpython-38.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/pysqlcipher.cpython-38.pyc,, +sqlalchemy/dialects/sqlite/__pycache__/pysqlite.cpython-38.pyc,, +sqlalchemy/dialects/sqlite/aiosqlite.py,sha256=XBpRrOb8zD5tMmZrdLxZ3MlQKG35JfcK7Muel8Z5Yzo,9995 +sqlalchemy/dialects/sqlite/base.py,sha256=h41saLgOroxbo1wLRjkc-x7m-N1bec6KKXrXIz_s9rA,88332 +sqlalchemy/dialects/sqlite/dml.py,sha256=A60UFadXJ7erPfg6xghfPrgQwCRcOYa1EUVxmdmdd04,6839 +sqlalchemy/dialects/sqlite/json.py,sha256=oFw4Rt8xw-tkD3IMlm3TDEGe1RqrTyvIuqjABsxn8EI,2518 +sqlalchemy/dialects/sqlite/provision.py,sha256=AQILXN5PBUSM05c-SFSFFhPdFqcQDwdoKtUnvLDac14,4676 +sqlalchemy/dialects/sqlite/pysqlcipher.py,sha256=oO4myPd2OOf8ACKlyofjEV95PonyFF3l6jdXezLh9Tw,5605 +sqlalchemy/dialects/sqlite/pysqlite.py,sha256=QfvICDp6cW4myH8eMOlygSKkwv7nlfgEc5yHdB1qL4o,23441 +sqlalchemy/dialects/sybase/__init__.py,sha256=_MpFLF4UYNG1YWxabCKP5p5_a2T8aYQdGb4zOgisSjE,1364 +sqlalchemy/dialects/sybase/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/dialects/sybase/__pycache__/base.cpython-38.pyc,, +sqlalchemy/dialects/sybase/__pycache__/mxodbc.cpython-38.pyc,, +sqlalchemy/dialects/sybase/__pycache__/pyodbc.cpython-38.pyc,, +sqlalchemy/dialects/sybase/__pycache__/pysybase.cpython-38.pyc,, +sqlalchemy/dialects/sybase/base.py,sha256=eTB8YYJm5iN-52e3X89zSfXGk_b6TVjbrBMSJgjrYwU,32421 +sqlalchemy/dialects/sybase/mxodbc.py,sha256=Y3ws2Ahe8yzcnzYeclQmujCgmIMK4Lm4tWtAFmo2IaI,939 +sqlalchemy/dialects/sybase/pyodbc.py,sha256=XV9fGWBFKSalUlNtWhRqnfdPzGknrt6Yr4D8yeRRV1E,2230 +sqlalchemy/dialects/sybase/pysybase.py,sha256=8V3fvp1R52o1DLzri8kZ5LLkXp68knyJ6CkwI_mIHoo,3370 +sqlalchemy/engine/__init__.py,sha256=ZHMG_4TdVFEzXuvOAKx_yEtmVMZHnfOyUd3XGQLFivU,2075 +sqlalchemy/engine/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/engine/__pycache__/base.cpython-38.pyc,, +sqlalchemy/engine/__pycache__/characteristics.cpython-38.pyc,, +sqlalchemy/engine/__pycache__/create.cpython-38.pyc,, +sqlalchemy/engine/__pycache__/cursor.cpython-38.pyc,, +sqlalchemy/engine/__pycache__/default.cpython-38.pyc,, +sqlalchemy/engine/__pycache__/events.cpython-38.pyc,, +sqlalchemy/engine/__pycache__/interfaces.cpython-38.pyc,, +sqlalchemy/engine/__pycache__/mock.cpython-38.pyc,, +sqlalchemy/engine/__pycache__/reflection.cpython-38.pyc,, +sqlalchemy/engine/__pycache__/result.cpython-38.pyc,, +sqlalchemy/engine/__pycache__/row.cpython-38.pyc,, +sqlalchemy/engine/__pycache__/strategies.cpython-38.pyc,, +sqlalchemy/engine/__pycache__/url.cpython-38.pyc,, +sqlalchemy/engine/__pycache__/util.cpython-38.pyc,, +sqlalchemy/engine/base.py,sha256=EmbRi24NQzwQkcKBmtrCc1PGD0FeQhWp4AScExrOS1k,119154 +sqlalchemy/engine/characteristics.py,sha256=qvd3T8HW470kIxN-x6OzycfjCFdnmbzcaFQeds7KHOw,1817 +sqlalchemy/engine/create.py,sha256=fYuff7D1cxS-dDg4_NyUHKaP66ckCrstsxC4pPkkTA4,31286 +sqlalchemy/engine/cursor.py,sha256=AoHt8Cg3NnC6aoxz19dw9thSxDAD5gObZRumCFBfOYM,68088 +sqlalchemy/engine/default.py,sha256=dG8lcErMCX58XoGLu30PQ5NJ-vNQB13hxaEVYa300vQ,64692 +sqlalchemy/engine/events.py,sha256=mnGuGKbF4zAxgSTVc0NHfGQvn7Vpp47pslGION-AaAU,33166 +sqlalchemy/engine/interfaces.py,sha256=ElnlzlTWNZKZaTQdQvwOff_U3Ao9tvNGssmnu0Nn5DM,60162 +sqlalchemy/engine/mock.py,sha256=37RtWX1xT7K1rem2jUtrKSynAb6HGxaq1YTW5iHAiSk,3626 +sqlalchemy/engine/reflection.py,sha256=WbFLy7C9WaPwlTVzAqtKzPcOBVOOUM251y06F5iIhkQ,38703 +sqlalchemy/engine/result.py,sha256=5Kwnd4pfrER9P5-20YCn4wc1Jg_JyLVkZv8JAADUyvU,55999 +sqlalchemy/engine/row.py,sha256=uHCGnP2Buf80pQvM45-uE5znJetdVMKtjs2u2fzaXls,18191 +sqlalchemy/engine/strategies.py,sha256=mfpCWvmkLxZUW6TkJriTqCOJF7VCgDZXgzaqLsxauBc,414 +sqlalchemy/engine/url.py,sha256=kzQFzB5ecS2-940PCO4OfL_76MsjnZoNrEmEO5cxsCQ,26298 +sqlalchemy/engine/util.py,sha256=wFS1ZCg9AWlwDqc8zcOXYVjptwyqITRLOlgp6b1Sagw,7719 +sqlalchemy/event/__init__.py,sha256=1QT0XxdMGwMMdoLzx58dUn4HqKNvzEysfKkPOluQECY,517 +sqlalchemy/event/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/event/__pycache__/api.cpython-38.pyc,, +sqlalchemy/event/__pycache__/attr.cpython-38.pyc,, +sqlalchemy/event/__pycache__/base.cpython-38.pyc,, +sqlalchemy/event/__pycache__/legacy.cpython-38.pyc,, +sqlalchemy/event/__pycache__/registry.cpython-38.pyc,, +sqlalchemy/event/api.py,sha256=yvpDdd2Yefw4B1enmW1rNO6Z_aW-YhpsswhnEl5KuUw,8043 +sqlalchemy/event/attr.py,sha256=gXcuUY3EaoWjCq2Q5Keg0O_yjmI_FvxlaCUL6ko7JgA,14625 +sqlalchemy/event/base.py,sha256=i5ud1V77ViLUQJIO_-ENEbK1VEM8lkhqmRcXrk7rZJQ,10936 +sqlalchemy/event/legacy.py,sha256=kt_rKWVIHSPQvlRSkw4NwgLf5Oz7xahXaaW-OYbmB4g,6270 +sqlalchemy/event/registry.py,sha256=pCfpcG80P6C3m-iQReVNNTc_OKQllM1CL0AAtUl_CcU,8486 +sqlalchemy/events.py,sha256=SFtMYfSRcdOmXAUvLZ_KoQfA5bHGxLW-YnaCL2xILlM,467 +sqlalchemy/exc.py,sha256=LH9UIgl1XRI8DijZI620eD88ZnZFPHQ3Ed7D4uIfRmg,21116 +sqlalchemy/ext/__init__.py,sha256=3eg5n6pdQubMMU1UzaNNRpSxb8e3B4fAuhpmQ3v4kx4,322 +sqlalchemy/ext/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/ext/__pycache__/associationproxy.cpython-38.pyc,, +sqlalchemy/ext/__pycache__/automap.cpython-38.pyc,, +sqlalchemy/ext/__pycache__/baked.cpython-38.pyc,, +sqlalchemy/ext/__pycache__/compiler.cpython-38.pyc,, +sqlalchemy/ext/__pycache__/horizontal_shard.cpython-38.pyc,, +sqlalchemy/ext/__pycache__/hybrid.cpython-38.pyc,, +sqlalchemy/ext/__pycache__/indexable.cpython-38.pyc,, +sqlalchemy/ext/__pycache__/instrumentation.cpython-38.pyc,, +sqlalchemy/ext/__pycache__/mutable.cpython-38.pyc,, +sqlalchemy/ext/__pycache__/orderinglist.cpython-38.pyc,, +sqlalchemy/ext/__pycache__/serializer.cpython-38.pyc,, +sqlalchemy/ext/associationproxy.py,sha256=PmtfYj17pDhj0XNZTzzIiL2RBjc6ipgsypcuVjNnkAs,50568 +sqlalchemy/ext/asyncio/__init__.py,sha256=YzmnHWOudsK1IMLNP-eCMqEkL3jvaXQRrslGczH22-4,778 +sqlalchemy/ext/asyncio/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/ext/asyncio/__pycache__/base.cpython-38.pyc,, +sqlalchemy/ext/asyncio/__pycache__/engine.cpython-38.pyc,, +sqlalchemy/ext/asyncio/__pycache__/events.cpython-38.pyc,, +sqlalchemy/ext/asyncio/__pycache__/exc.cpython-38.pyc,, +sqlalchemy/ext/asyncio/__pycache__/result.cpython-38.pyc,, +sqlalchemy/ext/asyncio/__pycache__/scoping.cpython-38.pyc,, +sqlalchemy/ext/asyncio/__pycache__/session.cpython-38.pyc,, +sqlalchemy/ext/asyncio/base.py,sha256=VQmIq-CMEVQpZPMEa0K91tMxZMqKyCCAwJVuCLiG34w,2280 +sqlalchemy/ext/asyncio/engine.py,sha256=FVHbGqs8PlfxHHnuUM44hsARUjH4v6BqXm44M1oF4HE,26154 +sqlalchemy/ext/asyncio/events.py,sha256=616mp5MMyCF4OiOAp794L0tGMKmp-mTtbwukTqQ3-bo,1235 +sqlalchemy/ext/asyncio/exc.py,sha256=DwS55QWrcgoThCC3w-kE5xVnl8kUAiWm1NVcyuO0Ufs,639 +sqlalchemy/ext/asyncio/result.py,sha256=eE5nM4fzGcKD0FKg6qVnbSa8RmUKejJ96rAo7kVqdeA,20438 +sqlalchemy/ext/asyncio/scoping.py,sha256=KvNCLokPgm8MOoyPeEpF-WctrJVynp2gibNFAUNj3pE,2937 +sqlalchemy/ext/asyncio/session.py,sha256=q-1sMnuCNLGZrAiGXZfqpC13D3tUCAL_wYP5af-zZOc,19853 +sqlalchemy/ext/automap.py,sha256=JJqJDyPp9p7sl4htcDG_RWBdPAE6SOfxTlGFO0bAcFU,45195 +sqlalchemy/ext/baked.py,sha256=OzOdFF9Wvz9sflF2EYlIEHP9tKbVn3x8K6pEEgM4Kg4,19969 +sqlalchemy/ext/compiler.py,sha256=XnPSC8_mQTSYTXOSegt0-XpPxZXzJHyTCpQvdVG-WtE,17989 +sqlalchemy/ext/declarative/__init__.py,sha256=M4hGt8MVZzjVespP-G_3lUP1oCk9rev_xN5AjSgB6TU,1842 +sqlalchemy/ext/declarative/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/ext/declarative/__pycache__/extensions.cpython-38.pyc,, +sqlalchemy/ext/declarative/extensions.py,sha256=bNylndJ-MdWBC1gn5dS5WUzgfvsDT-0r1Gqfl6EUAJI,16409 +sqlalchemy/ext/horizontal_shard.py,sha256=KuqRl1GStQmcAfJ2bFf08kbV8Dktx1jYZ_ogf_FZAkI,8922 +sqlalchemy/ext/hybrid.py,sha256=UYrun9R3vzShnkcX43-8jVRyqOWF6PcqMICF_ebbbMs,41787 +sqlalchemy/ext/indexable.py,sha256=mOjILC84bSHxehal-E893YJLEELTYPz7MD8DHIRFCr4,11255 +sqlalchemy/ext/instrumentation.py,sha256=ReSLFxqbHgwAKNwoQQmKHoqYvWCob_WuXlPAEUJk4pk,14386 +sqlalchemy/ext/mutable.py,sha256=3ZfxmQoctFchZNGJppg-bzxPPSLvLcGKt_k6AQgDTXI,31997 +sqlalchemy/ext/mypy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sqlalchemy/ext/mypy/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/ext/mypy/__pycache__/apply.cpython-38.pyc,, +sqlalchemy/ext/mypy/__pycache__/decl_class.cpython-38.pyc,, +sqlalchemy/ext/mypy/__pycache__/infer.cpython-38.pyc,, +sqlalchemy/ext/mypy/__pycache__/names.cpython-38.pyc,, +sqlalchemy/ext/mypy/__pycache__/plugin.cpython-38.pyc,, +sqlalchemy/ext/mypy/__pycache__/util.cpython-38.pyc,, +sqlalchemy/ext/mypy/apply.py,sha256=z42HLeqy5Hh9v--2ohql16fa5Lbsas0UZMYeraovq6w,9503 +sqlalchemy/ext/mypy/decl_class.py,sha256=D_12pRqsO5lLw_FovRrDAykbeEirv4l2wuWLMHyWA1c,16698 +sqlalchemy/ext/mypy/infer.py,sha256=s1GqaZSbjXsnS4fdMDuPRQZ3tIjSwj4Zz_xhvYOqNH0,17742 +sqlalchemy/ext/mypy/names.py,sha256=CZjTn0YTsR-XN7gMBD-D39PolKQsDQCwcjk9loAoM5M,7687 +sqlalchemy/ext/mypy/plugin.py,sha256=HTyHlZeSzcUMT86TTpcjWOsXCe128k1ZJoLIPgU56qU,9223 +sqlalchemy/ext/mypy/util.py,sha256=z1z8UewpjK7xC9Kl8wFooikNn7xbQiMrYJHkTTwxJog,8149 +sqlalchemy/ext/orderinglist.py,sha256=pAhYXNDVm0o0ZuxtbmGFan3Fw8zhpJhiRPmrndzM-_8,13875 +sqlalchemy/ext/serializer.py,sha256=i2HZTt9O-PxidEXZKb9iDqJO3F0uhQ4w6Ens48wM6gY,5956 +sqlalchemy/future/__init__.py,sha256=b1swUP9MZmoZx3VXv6aQ2L9JB5iThBQe09SviZP8HYo,525 +sqlalchemy/future/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/future/__pycache__/engine.cpython-38.pyc,, +sqlalchemy/future/engine.py,sha256=Ly-M3NGamVrpnA9XOG_nVLra5f7OlmTMmg7dMb2tn4s,16184 +sqlalchemy/future/orm/__init__.py,sha256=Fj72ozD2mgP9R9t8E6vWarr5USz_5AUx7BqWLEld84w,289 +sqlalchemy/future/orm/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/inspection.py,sha256=EqHUnvpjuwiPUIdD92autVgiO2YAgC-yX9Trk1m5oSA,3051 +sqlalchemy/log.py,sha256=G-jGx-_08ZUS2J3djgTgt-coqb4fngSl6ehYaF7nmYE,6770 +sqlalchemy/orm/__init__.py,sha256=XrV7_csV7aCMCHFmRDfWgMjZmuoLOnACuL1FFWdLFAo,10714 +sqlalchemy/orm/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/attributes.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/base.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/clsregistry.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/collections.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/context.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/decl_api.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/decl_base.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/dependency.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/descriptor_props.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/dynamic.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/evaluator.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/events.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/exc.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/identity.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/instrumentation.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/interfaces.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/loading.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/mapper.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/path_registry.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/persistence.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/properties.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/query.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/relationships.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/scoping.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/session.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/state.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/strategies.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/strategy_options.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/sync.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/unitofwork.cpython-38.pyc,, +sqlalchemy/orm/__pycache__/util.cpython-38.pyc,, +sqlalchemy/orm/attributes.py,sha256=-wzQhvx7ebUXcTWLlSo5yNNIXlVYEO-v3UJSrNF07ow,76590 +sqlalchemy/orm/base.py,sha256=Nuo2suXVjo_GPl7p5alHe9o5862VtXQ7Mv2IEx2r-fQ,15068 +sqlalchemy/orm/clsregistry.py,sha256=POxg7BYwk8FkuMHPi8dmgBpCGSY3RzMdOm0iHJBLAcY,13291 +sqlalchemy/orm/collections.py,sha256=b6wKDA6IKCkItw6gKOmFfjXv7-UUrPXi8UaIWb8S2M4,54711 +sqlalchemy/orm/context.py,sha256=kBlYs-1xchSwKewTcQ_1j3lpR1S1y2ycDp77eIr9-1o,106495 +sqlalchemy/orm/decl_api.py,sha256=_eLE7TI3omhAKv_X_4nHZzkdFnntlpNLLMczBeKKcz8,34976 +sqlalchemy/orm/decl_base.py,sha256=aG4x39xwWl8mQmDBMlAyhdhGBbX0GF2ylSiML7Z1BRY,43322 +sqlalchemy/orm/dependency.py,sha256=lLqfIMcBWk4ot9qhrNjoOce5-m0ciJSHJtPX5oHwGHs,46987 +sqlalchemy/orm/descriptor_props.py,sha256=L1-Fpt0ss2zd9yV-HuVshnA343mB5Ous2sPlEVviCqM,25987 +sqlalchemy/orm/dynamic.py,sha256=lKXKi3VlFFLYvTLGxQBovSs_87wn1Nb5BwiG5AieMug,15819 +sqlalchemy/orm/evaluator.py,sha256=dwZ9jDx4Ooay0lBs2mL5RjLj2fisUaNrwFWkYJtsS1Y,6852 +sqlalchemy/orm/events.py,sha256=xKIVaKg14lk1o3s4NWtIuAUnQw3ZPHP92Ve5zb_zCZg,110251 +sqlalchemy/orm/exc.py,sha256=3HLZcpE8ESh37Mzx711_PMhgQLUPzy2bX1-RVA2o8xw,6532 +sqlalchemy/orm/identity.py,sha256=GOb7b8he7SslK6Qd3CjjIThUI2eT4IPU6FM3buVFC-M,7233 +sqlalchemy/orm/instrumentation.py,sha256=-BxrpgaW-pznyd-9bsRM8ns69fGaojdO5qAxnHHz5Pw,20349 +sqlalchemy/orm/interfaces.py,sha256=dqN7rQfF-0LRsx0y9r2ya6BXRjS4wtuh_DTRyDZT0qI,28791 +sqlalchemy/orm/loading.py,sha256=a9a9cLOUi2vFgW_Sg2yelTRlJw_4PbJ6iJp6iRFDoK0,49316 +sqlalchemy/orm/mapper.py,sha256=RDXIXT1g5blx4uXC5N9CxsaUi8paDp8mfcR8CLnLlO0,139136 +sqlalchemy/orm/path_registry.py,sha256=_dDIuYcBdQoGjY2turOru31SDY8tORNk-Qa3LwMG-zw,16411 +sqlalchemy/orm/persistence.py,sha256=6sLRZp5aTgSlSEKE_m1YRT3vuFtb8B9URjUIeMyo--A,80422 +sqlalchemy/orm/properties.py,sha256=EPhbfqvoqrN7X76guY3Wl5LO2yIl9FQBXiVrRgH5RX0,14912 +sqlalchemy/orm/query.py,sha256=kqz6_2371rQIoHBE3OiOdg6NggfccVLyt5jMSV9Cq7Q,124763 +sqlalchemy/orm/relationships.py,sha256=7RS2NC565MGVh3duADE45yAW_u3bwUi3X6mLGd4rMNg,143340 +sqlalchemy/orm/scoping.py,sha256=AKNozuMi8T-YB7DDNeatnVIVVhJ0FxSCbP1DBBALUh0,7220 +sqlalchemy/orm/session.py,sha256=Dn3sv9-uDTR9sPdF1t4i_uU5u5G6OoAFrG3MQH56gM8,158731 +sqlalchemy/orm/state.py,sha256=actQlG4fvVtEapQxIObZDw6T5wfPPCCnDxwIaW51OUs,33409 +sqlalchemy/orm/strategies.py,sha256=zPEzy7xptnOEvSIXpEKGeG8ltdoCtcoS-aXxted1u2k,107887 +sqlalchemy/orm/strategy_options.py,sha256=Y5yWvCpLILolcjvaYK8RzwsGeM2rCevCtiBipQRcHb0,66753 +sqlalchemy/orm/sync.py,sha256=tE2dS0i3vekS1TfB7R-_hhvekOi_esfcB-0bSwajjck,5824 +sqlalchemy/orm/unitofwork.py,sha256=nRJ7fWzpiedk5ObQz2v5gojLBzml9W5Al4qNB6-JWoI,27090 +sqlalchemy/orm/util.py,sha256=EZNS_6y0aV75RPvv3w04yUlfFS6DlLsM8-2pfTCAWLM,72410 +sqlalchemy/pool/__init__.py,sha256=cQIwYAY52VyVgAKdA159zhdlS38Dy6fFWT7l-KWjubk,1603 +sqlalchemy/pool/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/pool/__pycache__/base.cpython-38.pyc,, +sqlalchemy/pool/__pycache__/dbapi_proxy.cpython-38.pyc,, +sqlalchemy/pool/__pycache__/events.cpython-38.pyc,, +sqlalchemy/pool/__pycache__/impl.cpython-38.pyc,, +sqlalchemy/pool/base.py,sha256=vKBUGS59GwHjbQu-9ZFLzRbAqowTa-UgL9pjPKnUwYg,38552 +sqlalchemy/pool/dbapi_proxy.py,sha256=mPGtLr9czWrlVm2INYS1yMDr8bx-8rxY4KbAKmAasTk,4229 +sqlalchemy/pool/events.py,sha256=Z4LB7zGyEh-6L5VcWLdSdZ9lfOgDnn_G939Y_Fx_Wc8,10046 +sqlalchemy/pool/impl.py,sha256=VpW58L1fSxPtXWAzPvZ8qFhDRd_QiLdXN5GcjG68KP4,15783 +sqlalchemy/processors.py,sha256=ZnVfpn3-SQyqBa-3bmrjVPu3WyB7wsCovqRAeQdOP0M,5745 +sqlalchemy/schema.py,sha256=SbqBYd5vstujoWgpBXav9hBz7uUernJlhDTMFE05b4s,2413 +sqlalchemy/sql/__init__.py,sha256=E8Itj6nV7prc9wxhZiLBNghWLgG-MplZv_K3kxPltfc,4661 +sqlalchemy/sql/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/annotation.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/base.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/coercions.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/compiler.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/crud.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/ddl.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/default_comparator.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/dml.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/elements.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/events.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/expression.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/functions.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/lambdas.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/naming.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/operators.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/roles.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/schema.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/selectable.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/sqltypes.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/traversals.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/type_api.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/util.cpython-38.pyc,, +sqlalchemy/sql/__pycache__/visitors.cpython-38.pyc,, +sqlalchemy/sql/annotation.py,sha256=90IWQimMll92PwzUzP1I0zX_DQBSreciHZspCIj1Hro,11235 +sqlalchemy/sql/base.py,sha256=QSJLUi-78qBt8DCt96aQ1BeNx8RgkC18lJ2fW6phWtc,55804 +sqlalchemy/sql/coercions.py,sha256=YG6Qxn3jdOGaTXxU9KYAMsXewiFt6hDqlaI4vQVH184,33982 +sqlalchemy/sql/compiler.py,sha256=gJW34BvzWRiMj-21RVuy9mg8H2XjL6dui0NcoLHnIh0,185150 +sqlalchemy/sql/crud.py,sha256=LvT3ktKZ1eRvA85k_xXdP2ReU8-EWRYFMJhk-B0uRmc,35420 +sqlalchemy/sql/ddl.py,sha256=mHHvmrcCuLhU3B5FitlOzN_2OTer4NX2lIUUPwRlp7U,44002 +sqlalchemy/sql/default_comparator.py,sha256=cWeQCeJExjrR1WE_kLSbOD2wXRPpNzWQ7e7WLCtqknc,11046 +sqlalchemy/sql/dml.py,sha256=IJtieRwwROzAOCGcxMsIR2BCo-7H5mslZNMBzYRs138,51654 +sqlalchemy/sql/elements.py,sha256=5UuBCaLQnhZJauf-oc86bMtNnqscZZg5DCHJs1QhTbg,179644 +sqlalchemy/sql/events.py,sha256=Fjlr6Bl-dWFyUJfJaSB5fzhrkGil4oNuVRiCGax05nA,13369 +sqlalchemy/sql/expression.py,sha256=U4nrgSoREcBFP9gjaT2SZByHsls4JS1TIBpobKv1Y7c,8828 +sqlalchemy/sql/functions.py,sha256=bZVBP3oNRQUHZOqVtXHeEywJZKWWfVoGLdg-Kdwau1Q,47344 +sqlalchemy/sql/lambdas.py,sha256=vl2xjN6EgnzIPVDDqDhMw7M7LMK1QvMfJ3Bvw2XDl7c,44238 +sqlalchemy/sql/naming.py,sha256=G1eXvRjbZ8QENRaOhSIj_8ZFAiqeqe4hHPpBKktXmns,6786 +sqlalchemy/sql/operators.py,sha256=HgEviZ28iLSRcsE343HUXhESUzVR_TaENpx3msuXbOM,47433 +sqlalchemy/sql/roles.py,sha256=bE-DYltzs2IGuzgY-4pzHnFdwH0EFqc4o88j178IF8E,5526 +sqlalchemy/sql/schema.py,sha256=cBzp3Zx40eGmGPoof98Uz5Fzqv80FTux3iJVOSeIyoo,188537 +sqlalchemy/sql/selectable.py,sha256=19rB7zrUqpjk3MFV2barBCrGoCP82Mn5h8L67RrcRiY,230760 +sqlalchemy/sql/sqltypes.py,sha256=tioX3SDE_uu-cCLbDjIC5-miUrlJkZqslcttEUiVoII,111862 +sqlalchemy/sql/traversals.py,sha256=vgbnT_A8cSgf5a23IXn8Qj3HkJJ2rpvIhyWLqLKeDco,49443 +sqlalchemy/sql/type_api.py,sha256=7QCCVwZ8QPzO_r2Vj-t7C47KVw3Utw3-AOOo6vHhMY0,64262 +sqlalchemy/sql/util.py,sha256=gS1-RY_-tU-UuZneIahlOUFV2irE6m6OX9eDSMMC6b4,35856 +sqlalchemy/sql/visitors.py,sha256=uPsWctSGvEC77lCGO2SgrIs6GONnIT0kkU6--SMrHvc,27316 +sqlalchemy/testing/__init__.py,sha256=2r5jKsKug5mSBWqc8szFQZjT-SEQ0S00ZUkDhvMaGaE,2768 +sqlalchemy/testing/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/testing/__pycache__/assertions.cpython-38.pyc,, +sqlalchemy/testing/__pycache__/assertsql.cpython-38.pyc,, +sqlalchemy/testing/__pycache__/asyncio.cpython-38.pyc,, +sqlalchemy/testing/__pycache__/config.cpython-38.pyc,, +sqlalchemy/testing/__pycache__/engines.cpython-38.pyc,, +sqlalchemy/testing/__pycache__/entities.cpython-38.pyc,, +sqlalchemy/testing/__pycache__/exclusions.cpython-38.pyc,, +sqlalchemy/testing/__pycache__/fixtures.cpython-38.pyc,, +sqlalchemy/testing/__pycache__/mock.cpython-38.pyc,, +sqlalchemy/testing/__pycache__/pickleable.cpython-38.pyc,, +sqlalchemy/testing/__pycache__/profiling.cpython-38.pyc,, +sqlalchemy/testing/__pycache__/provision.cpython-38.pyc,, +sqlalchemy/testing/__pycache__/requirements.cpython-38.pyc,, +sqlalchemy/testing/__pycache__/schema.cpython-38.pyc,, +sqlalchemy/testing/__pycache__/util.cpython-38.pyc,, +sqlalchemy/testing/__pycache__/warnings.cpython-38.pyc,, +sqlalchemy/testing/assertions.py,sha256=vLuuP0Re7mg8aozEtk9GkKyN2tAcsYN7HZ-8rvCeDj8,24469 +sqlalchemy/testing/assertsql.py,sha256=VDeFE6B6MUOsW46Wjpef48ypTfbkwx1glm6ExuiZ28g,14964 +sqlalchemy/testing/asyncio.py,sha256=ffDzERQV3g2cpylQHdfuc1ZveK7v_Q8240cCdsaEFAk,3672 +sqlalchemy/testing/config.py,sha256=BokuYTNp-Nkcjb-x_IaF-FU869ONJE3k_wv52n7ojZ4,6543 +sqlalchemy/testing/engines.py,sha256=XOHhutDz2RHqfuVtzQxNlgrY4T8n5QqlwK4YOcdvZZs,12661 +sqlalchemy/testing/entities.py,sha256=lxagTVr0pqQb14fr3-pdpbHXSxlbYh5UK-jLazQcd3Q,3253 +sqlalchemy/testing/exclusions.py,sha256=i-QZY81gdxRQZ-TF5I_I2Q6P4iSJqPCIdCMpNVwAvTE,13329 +sqlalchemy/testing/fixtures.py,sha256=Y9JYGCnkgU2jXRQAYE3fj9uq5fGl0pRE3sjkkTSbsrc,25641 +sqlalchemy/testing/mock.py,sha256=bw0Ds9eMMBHEDzT6shKJxi-9fFMH6qB9D00QxedH4OY,894 +sqlalchemy/testing/pickleable.py,sha256=0Rfbbtj7LJIsYOKo_cbByUC4FnXYXLiXwHl1VwrtcW8,2707 +sqlalchemy/testing/plugin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +sqlalchemy/testing/plugin/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/testing/plugin/__pycache__/bootstrap.cpython-38.pyc,, +sqlalchemy/testing/plugin/__pycache__/plugin_base.cpython-38.pyc,, +sqlalchemy/testing/plugin/__pycache__/pytestplugin.cpython-38.pyc,, +sqlalchemy/testing/plugin/__pycache__/reinvent_fixtures_py2k.cpython-38.pyc,, +sqlalchemy/testing/plugin/bootstrap.py,sha256=0YdnGTKGRs2OKCD3dUD4ShTZ2MR8ikUHdrn4OSSROjI,1686 +sqlalchemy/testing/plugin/plugin_base.py,sha256=u9dgPRH9Q2y46j3MPYqePCnClVp-9ejUc2vg1s1016k,21545 +sqlalchemy/testing/plugin/pytestplugin.py,sha256=LivS94F7FeYzDP-O-lHAm-fqsVnMRIbXQTfByw1E6NA,25560 +sqlalchemy/testing/plugin/reinvent_fixtures_py2k.py,sha256=MdakbJzFh8N_7gUpX-nFbGPFs3AZRsmDAe-7zucf0ls,3288 +sqlalchemy/testing/profiling.py,sha256=q_4rhjMpb0nWBZ7K_JkuQMLKPcI-1kiB27_EKI49CDw,10566 +sqlalchemy/testing/provision.py,sha256=YUEX9eiHBnQYpTHKBWM9IBMoVRFIgm6sjcZIqOeyKIc,12047 +sqlalchemy/testing/requirements.py,sha256=A5cZGfEHGE1BcvSkciHmTy7_UyrdZjT7NNHK5JK0_yw,42342 +sqlalchemy/testing/schema.py,sha256=0IHnIuEHYMqjdSIjMkn7dUKSZoWbY7ou4SWGQY5X13o,6544 +sqlalchemy/testing/suite/__init__.py,sha256=_firVc2uS3TMZ3vH2baQzNb17ubM78RHtb9kniSybmk,476 +sqlalchemy/testing/suite/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/testing/suite/__pycache__/test_cte.cpython-38.pyc,, +sqlalchemy/testing/suite/__pycache__/test_ddl.cpython-38.pyc,, +sqlalchemy/testing/suite/__pycache__/test_deprecations.cpython-38.pyc,, +sqlalchemy/testing/suite/__pycache__/test_dialect.cpython-38.pyc,, +sqlalchemy/testing/suite/__pycache__/test_insert.cpython-38.pyc,, +sqlalchemy/testing/suite/__pycache__/test_reflection.cpython-38.pyc,, +sqlalchemy/testing/suite/__pycache__/test_results.cpython-38.pyc,, +sqlalchemy/testing/suite/__pycache__/test_rowcount.cpython-38.pyc,, +sqlalchemy/testing/suite/__pycache__/test_select.cpython-38.pyc,, +sqlalchemy/testing/suite/__pycache__/test_sequence.cpython-38.pyc,, +sqlalchemy/testing/suite/__pycache__/test_types.cpython-38.pyc,, +sqlalchemy/testing/suite/__pycache__/test_unicode_ddl.cpython-38.pyc,, +sqlalchemy/testing/suite/__pycache__/test_update_delete.cpython-38.pyc,, +sqlalchemy/testing/suite/test_cte.py,sha256=XuTuaWblSXyO1OOUTShBBmNch7fBdGnlMD84ooVTqFY,6183 +sqlalchemy/testing/suite/test_ddl.py,sha256=UwbfljXHdWUen3muIcgnOPi-A4AO6F1QzSOiHf9lU-A,11762 +sqlalchemy/testing/suite/test_deprecations.py,sha256=8oLDFUswey8KjPFKRUsqMyGT5sUMMoPQr7-XyIBMehw,5059 +sqlalchemy/testing/suite/test_dialect.py,sha256=eR1VVOb2fm955zavpWkmMjipCva3QvEE177U0OG-0LY,10895 +sqlalchemy/testing/suite/test_insert.py,sha256=oKtVjFuxqdSV5uKj5-OxdSABupLp0pECkWkSLd2U_QA,11134 +sqlalchemy/testing/suite/test_reflection.py,sha256=BJkKloxhujHSTNuwL29EyiSTd2A77ZJhTtc0GrBcnd0,57615 +sqlalchemy/testing/suite/test_results.py,sha256=xcoSl1ueaHo8LgKZp0Z1lJ44Mhjf2hxlWs_LjNLBNiE,13983 +sqlalchemy/testing/suite/test_rowcount.py,sha256=GQQRXIWbb6SfD5hwtBC8qvkGAgi1rI5Pv3c59eoumck,4877 +sqlalchemy/testing/suite/test_select.py,sha256=is3BbULeOWOJTRCoUwPnh6Crue15FXfkXKqAkxrFeGM,55464 +sqlalchemy/testing/suite/test_sequence.py,sha256=eCyOQlynF8T0cLrIMz0PO6WuW8ktpFVYq_fQp5CQ298,8431 +sqlalchemy/testing/suite/test_types.py,sha256=ZOWB06wLYK8sQc6RyZfB8y3GOViVlO6xF9UjmKS5F5A,45839 +sqlalchemy/testing/suite/test_unicode_ddl.py,sha256=CndeAtV3DWJXxLbOoumqf4_mOOYcW_yNOrbKQ4cwFhw,6737 +sqlalchemy/testing/suite/test_update_delete.py,sha256=ebU5oV9hUZCW1ZBaZ-YAnxQE2Nk6GQashkOy6FOsp_c,1587 +sqlalchemy/testing/util.py,sha256=ZtMew3LnhnKuL8V7oeQ9YC5rv4ZExSKdKh5VxVyjDj0,12503 +sqlalchemy/testing/warnings.py,sha256=BIE5FlG03PHlZ4oy-kx5IIO78q0m_rahAvW8pDJhoxc,2347 +sqlalchemy/types.py,sha256=u2qTsqsdmtsedDKSDS9B1JDEvKGtvmWgHLjrj-YvLyk,2936 +sqlalchemy/util/__init__.py,sha256=F39iHcW8H3sxjhJcZ2_Y0SRoKrLtIZzEB4Rwj6B79dQ,6347 +sqlalchemy/util/__pycache__/__init__.cpython-38.pyc,, +sqlalchemy/util/__pycache__/_collections.cpython-38.pyc,, +sqlalchemy/util/__pycache__/_compat_py3k.cpython-38.pyc,, +sqlalchemy/util/__pycache__/_concurrency_py3k.cpython-38.pyc,, +sqlalchemy/util/__pycache__/_preloaded.cpython-38.pyc,, +sqlalchemy/util/__pycache__/compat.cpython-38.pyc,, +sqlalchemy/util/__pycache__/concurrency.cpython-38.pyc,, +sqlalchemy/util/__pycache__/deprecations.cpython-38.pyc,, +sqlalchemy/util/__pycache__/langhelpers.cpython-38.pyc,, +sqlalchemy/util/__pycache__/queue.cpython-38.pyc,, +sqlalchemy/util/__pycache__/topological.cpython-38.pyc,, +sqlalchemy/util/_collections.py,sha256=BhJPIHmzZ56K35OdqUhxueitkG-_DXqq2VfNggPzD4U,29139 +sqlalchemy/util/_compat_py3k.py,sha256=VSDmbXqhWhW_z4ysSdIYbGW_nbhAJKbEEFUINLEGobI,2195 +sqlalchemy/util/_concurrency_py3k.py,sha256=SOgSeqpdQ6SM_o5HSBMRRWX5Att8c4w05oK18xy0nKY,6780 +sqlalchemy/util/_preloaded.py,sha256=SGizwMVpZcVk_4OFVBkYuB1ISaySciSstyel8OAptIk,2396 +sqlalchemy/util/compat.py,sha256=tk5GQ_Wh6DhUJ35FpbUfH9f-pVMMFi54Q8URlpYEOsI,18245 +sqlalchemy/util/concurrency.py,sha256=j5c0L2qNWjBzcORYZ5dcA16qPrw3RXnhbqBf7PppegI,2117 +sqlalchemy/util/deprecations.py,sha256=ycHV6JnmF1yoV_Ww_4kQ3L_RCksc7BXHLYHWuBArNHw,11729 +sqlalchemy/util/langhelpers.py,sha256=FxMJByLfGzagAkHqSGofTCN8GVSOmahtkuH1z_16Trw,56250 +sqlalchemy/util/queue.py,sha256=WvS8AimNmR8baB-QDbHJe9F4RT9e05bYLxiVPouzNLk,9293 +sqlalchemy/util/topological.py,sha256=FtPkCjm8J6RU3sHZqM5AmQZCsqHfGfugu41pU8GS35k,2859 diff --git a/flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/WHEEL b/flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/WHEEL new file mode 100644 index 000000000..d5e85bd31 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: false +Tag: cp38-cp38-macosx_10_14_x86_64 + diff --git a/flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/top_level.txt b/flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/top_level.txt new file mode 100644 index 000000000..39fb2befb --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/SQLAlchemy-1.4.27.dist-info/top_level.txt @@ -0,0 +1 @@ +sqlalchemy diff --git a/flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/INSTALLER b/flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/LICENSE.rst b/flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/LICENSE.rst new file mode 100644 index 000000000..c37cae49e --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2007 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/METADATA b/flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/METADATA new file mode 100644 index 000000000..b58b9bd5f --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/METADATA @@ -0,0 +1,129 @@ +Metadata-Version: 2.1 +Name: Werkzeug +Version: 2.0.2 +Summary: The comprehensive WSGI web application library. +Home-page: https://palletsprojects.com/p/werkzeug/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://werkzeug.palletsprojects.com/ +Project-URL: Changes, https://werkzeug.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/werkzeug/ +Project-URL: Issue Tracker, https://github.com/pallets/werkzeug/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Web Environment +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Application +Classifier: Topic :: Internet :: WWW/HTTP :: WSGI :: Middleware +Classifier: Topic :: Software Development :: Libraries :: Application Frameworks +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: dataclasses ; python_version < "3.7" +Provides-Extra: watchdog +Requires-Dist: watchdog ; extra == 'watchdog' + +Werkzeug +======== + +*werkzeug* German noun: "tool". Etymology: *werk* ("work"), *zeug* ("stuff") + +Werkzeug is a comprehensive `WSGI`_ web application library. It began as +a simple collection of various utilities for WSGI applications and has +become one of the most advanced WSGI utility libraries. + +It includes: + +- An interactive debugger that allows inspecting stack traces and + source code in the browser with an interactive interpreter for any + frame in the stack. +- A full-featured request object with objects to interact with + headers, query args, form data, files, and cookies. +- A response object that can wrap other WSGI applications and handle + streaming data. +- A routing system for matching URLs to endpoints and generating URLs + for endpoints, with an extensible system for capturing variables + from URLs. +- HTTP utilities to handle entity tags, cache control, dates, user + agents, cookies, files, and more. +- A threaded WSGI server for use while developing applications + locally. +- A test client for simulating HTTP requests during testing without + requiring running a server. + +Werkzeug doesn't enforce any dependencies. It is up to the developer to +choose a template engine, database adapter, and even how to handle +requests. It can be used to build all sorts of end user applications +such as blogs, wikis, or bulletin boards. + +`Flask`_ wraps Werkzeug, using it to handle the details of WSGI while +providing more structure and patterns for defining powerful +applications. + +.. _WSGI: https://wsgi.readthedocs.io/en/latest/ +.. _Flask: https://www.palletsprojects.com/p/flask/ + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + pip install -U Werkzeug + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +A Simple Example +---------------- + +.. code-block:: python + + from werkzeug.wrappers import Request, Response + + @Request.application + def application(request): + return Response('Hello, World!') + + if __name__ == '__main__': + from werkzeug.serving import run_simple + run_simple('localhost', 4000, application) + + +Donate +------ + +The Pallets organization develops and supports Werkzeug and other +popular packages. In order to grow the community of contributors and +users, and allow the maintainers to devote more time to the projects, +`please donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://werkzeug.palletsprojects.com/ +- Changes: https://werkzeug.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/Werkzeug/ +- Source Code: https://github.com/pallets/werkzeug/ +- Issue Tracker: https://github.com/pallets/werkzeug/issues/ +- Website: https://palletsprojects.com/p/werkzeug/ +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/RECORD b/flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/RECORD new file mode 100644 index 000000000..a566dd6fa --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/RECORD @@ -0,0 +1,111 @@ +Werkzeug-2.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +Werkzeug-2.0.2.dist-info/LICENSE.rst,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475 +Werkzeug-2.0.2.dist-info/METADATA,sha256=vh_xrARtpmkFYnWRAgfSiHgl66LH143rMfAfPZo-R_E,4452 +Werkzeug-2.0.2.dist-info/RECORD,, +Werkzeug-2.0.2.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 +Werkzeug-2.0.2.dist-info/top_level.txt,sha256=QRyj2VjwJoQkrwjwFIOlB8Xg3r9un0NtqVHQF-15xaw,9 +werkzeug/__init__.py,sha256=Wx1PLCftJ7UAS0fBXEO4Prdr6kvEQ124Stwg-XwyhW4,188 +werkzeug/__pycache__/__init__.cpython-38.pyc,, +werkzeug/__pycache__/_internal.cpython-38.pyc,, +werkzeug/__pycache__/_reloader.cpython-38.pyc,, +werkzeug/__pycache__/datastructures.cpython-38.pyc,, +werkzeug/__pycache__/exceptions.cpython-38.pyc,, +werkzeug/__pycache__/filesystem.cpython-38.pyc,, +werkzeug/__pycache__/formparser.cpython-38.pyc,, +werkzeug/__pycache__/http.cpython-38.pyc,, +werkzeug/__pycache__/local.cpython-38.pyc,, +werkzeug/__pycache__/routing.cpython-38.pyc,, +werkzeug/__pycache__/security.cpython-38.pyc,, +werkzeug/__pycache__/serving.cpython-38.pyc,, +werkzeug/__pycache__/test.cpython-38.pyc,, +werkzeug/__pycache__/testapp.cpython-38.pyc,, +werkzeug/__pycache__/urls.cpython-38.pyc,, +werkzeug/__pycache__/user_agent.cpython-38.pyc,, +werkzeug/__pycache__/useragents.cpython-38.pyc,, +werkzeug/__pycache__/utils.cpython-38.pyc,, +werkzeug/__pycache__/wsgi.cpython-38.pyc,, +werkzeug/_internal.py,sha256=_QKkvdaG4pDFwK68c0EpPzYJGe9Y7toRAT1cBbC-CxU,18572 +werkzeug/_reloader.py,sha256=B1hEfgsUOz2IginBQM5Zak_eaIF7gr3GS5-0x2OHvAE,13950 +werkzeug/datastructures.py,sha256=m79A8rHQEt5B7qVqyrjARXzHL66Katn8S92urGscTw4,97929 +werkzeug/datastructures.pyi,sha256=CoVwrQ2Vr9JnbprNL9aE3vOz8mOejT9qysQ-BT53C8Y,34089 +werkzeug/debug/__init__.py,sha256=jYA1e1Gw_8EPOytr-BoMdmm0rzP-Z1H0Ih7wIObnKwQ,17968 +werkzeug/debug/__pycache__/__init__.cpython-38.pyc,, +werkzeug/debug/__pycache__/console.cpython-38.pyc,, +werkzeug/debug/__pycache__/repr.cpython-38.pyc,, +werkzeug/debug/__pycache__/tbtools.cpython-38.pyc,, +werkzeug/debug/console.py,sha256=E1nBMEvFkX673ShQjPtVY-byYatfX9MN-dBMjRI8a8E,5897 +werkzeug/debug/repr.py,sha256=QCSHENKsChEZDCIApkVi_UNjhJ77v8BMXK1OfxO189M,9483 +werkzeug/debug/shared/FONT_LICENSE,sha256=LwAVEI1oYnvXiNMT9SnCH_TaLCxCpeHziDrMg0gPkAI,4673 +werkzeug/debug/shared/ICON_LICENSE.md,sha256=DhA6Y1gUl5Jwfg0NFN9Rj4VWITt8tUx0IvdGf0ux9-s,222 +werkzeug/debug/shared/console.png,sha256=bxax6RXXlvOij_KeqvSNX0ojJf83YbnZ7my-3Gx9w2A,507 +werkzeug/debug/shared/debugger.js,sha256=tg42SZs1SVmYWZ-_Fj5ELK5-FLHnGNQrei0K2By8Bw8,10521 +werkzeug/debug/shared/less.png,sha256=-4-kNRaXJSONVLahrQKUxMwXGm9R4OnZ9SxDGpHlIR4,191 +werkzeug/debug/shared/more.png,sha256=GngN7CioHQoV58rH6ojnkYi8c_qED2Aka5FO5UXrReY,200 +werkzeug/debug/shared/source.png,sha256=RoGcBTE4CyCB85GBuDGTFlAnUqxwTBiIfDqW15EpnUQ,818 +werkzeug/debug/shared/style.css,sha256=h1ZSUVaKNpfbfcYzRb513WAhPySGDQom1uih3uEDxPw,6704 +werkzeug/debug/shared/ubuntu.ttf,sha256=1eaHFyepmy4FyDvjLVzpITrGEBu_CZYY94jE0nED1c0,70220 +werkzeug/debug/tbtools.py,sha256=AFRrjLDCAps7G5K2-RxNZpXXaEoeFHm68T00f4vlDYA,19362 +werkzeug/exceptions.py,sha256=CUwx0pBiNbk4f9cON17ekgKnmLi6HIVFjUmYZc2x0wM,28681 +werkzeug/filesystem.py,sha256=JS2Dv2QF98WILxY4_thHl-WMcUcwluF_4igkDPaP1l4,1956 +werkzeug/formparser.py,sha256=X-p3Ek4ji8XrKrbmaWxr8StLSc6iuksbpIeweaabs4s,17400 +werkzeug/http.py,sha256=oUCXFFMnkOQ-cHbUY_aiqitshcrSzNDq3fEMf1VI_yk,45141 +werkzeug/local.py,sha256=bwL-y3-qOZAspJ66W1P36SUApLXJy3UY8nLYbM9kfmY,23183 +werkzeug/middleware/__init__.py,sha256=qfqgdT5npwG9ses3-FXQJf3aB95JYP1zchetH_T3PUw,500 +werkzeug/middleware/__pycache__/__init__.cpython-38.pyc,, +werkzeug/middleware/__pycache__/dispatcher.cpython-38.pyc,, +werkzeug/middleware/__pycache__/http_proxy.cpython-38.pyc,, +werkzeug/middleware/__pycache__/lint.cpython-38.pyc,, +werkzeug/middleware/__pycache__/profiler.cpython-38.pyc,, +werkzeug/middleware/__pycache__/proxy_fix.cpython-38.pyc,, +werkzeug/middleware/__pycache__/shared_data.cpython-38.pyc,, +werkzeug/middleware/dispatcher.py,sha256=Fh_w-KyWnTSYF-Lfv5dimQ7THSS7afPAZMmvc4zF1gg,2580 +werkzeug/middleware/http_proxy.py,sha256=HE8VyhS7CR-E1O6_9b68huv8FLgGGR1DLYqkS3Xcp3Q,7558 +werkzeug/middleware/lint.py,sha256=sAg3GcOhICIkwYX5bJGG8n8iebX0Yipq_UH0HvrBvoU,13964 +werkzeug/middleware/profiler.py,sha256=QkXk7cqnaPnF8wQu-5SyPCIOT3_kdABUBorQOghVNOA,4899 +werkzeug/middleware/proxy_fix.py,sha256=uRgQ3dEvFV8JxUqajHYYYOPEeA_BFqaa51Yp8VW0uzA,6849 +werkzeug/middleware/shared_data.py,sha256=xydEqOhAGg0aQJEllPDVfz2-8jHwWvJpAxfPsfPCu7k,10960 +werkzeug/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +werkzeug/routing.py,sha256=oqJ32sWIZtIF6zbqfrnwB1Pbv2ShNwPDJd6FYqxdYVo,84527 +werkzeug/sansio/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +werkzeug/sansio/__pycache__/__init__.cpython-38.pyc,, +werkzeug/sansio/__pycache__/multipart.cpython-38.pyc,, +werkzeug/sansio/__pycache__/request.cpython-38.pyc,, +werkzeug/sansio/__pycache__/response.cpython-38.pyc,, +werkzeug/sansio/__pycache__/utils.cpython-38.pyc,, +werkzeug/sansio/multipart.py,sha256=bJMCNC2f5xyAaylahNViJ0JqmV4ThLRbDVGVzKwcqrQ,8751 +werkzeug/sansio/request.py,sha256=aA9rABkWiG4MhYMByanst2NXkEclsq8SIxhb0LQf0e0,20228 +werkzeug/sansio/response.py,sha256=zvCq9HSBBZGBd5Gg412BY9RZIwnKsJl5Kzfd3Kl9sSo,26098 +werkzeug/sansio/utils.py,sha256=V5v-UUnX8pm4RehP9Tt_NiUSOJGJGUvKjlW0eOIQldM,4164 +werkzeug/security.py,sha256=gPDRuCjkjWrcqj99tBMq8_nHFZLFQjgoW5Ga5XIw9jo,8158 +werkzeug/serving.py,sha256=AfgLn0yKr9qXknmwO-0KXJ055oloS4h5DIFDHEu8iHA,38088 +werkzeug/test.py,sha256=8gE1l-Y9yAh2i3SI0kgpxIaI4oYZuehIkxxyDFcz6J0,48123 +werkzeug/testapp.py,sha256=f48prWSGJhbSrvYb8e1fnAah4BkrLb0enHSdChgsjBY,9471 +werkzeug/urls.py,sha256=Du2lreBHvgBh5c2_bcx72g3hzV2ZabXYZsp-picUIJs,41023 +werkzeug/user_agent.py,sha256=WclZhpvgLurMF45hsioSbS75H1Zb4iMQGKN3_yZ2oKo,1420 +werkzeug/useragents.py,sha256=G8tmv_6vxJaPrLQH3eODNgIYe0_V6KETROQlJI-WxDE,7264 +werkzeug/utils.py,sha256=D_dnCLUfodQ4k0GRSpnI6qDoVoaX7-Dza57bx7sabG0,37101 +werkzeug/wrappers/__init__.py,sha256=-s75nPbyXHzU_rwmLPDhoMuGbEUk0jZT_n0ZQAOFGf8,654 +werkzeug/wrappers/__pycache__/__init__.cpython-38.pyc,, +werkzeug/wrappers/__pycache__/accept.cpython-38.pyc,, +werkzeug/wrappers/__pycache__/auth.cpython-38.pyc,, +werkzeug/wrappers/__pycache__/base_request.cpython-38.pyc,, +werkzeug/wrappers/__pycache__/base_response.cpython-38.pyc,, +werkzeug/wrappers/__pycache__/common_descriptors.cpython-38.pyc,, +werkzeug/wrappers/__pycache__/cors.cpython-38.pyc,, +werkzeug/wrappers/__pycache__/etag.cpython-38.pyc,, +werkzeug/wrappers/__pycache__/json.cpython-38.pyc,, +werkzeug/wrappers/__pycache__/request.cpython-38.pyc,, +werkzeug/wrappers/__pycache__/response.cpython-38.pyc,, +werkzeug/wrappers/__pycache__/user_agent.cpython-38.pyc,, +werkzeug/wrappers/accept.py,sha256=_oZtAQkahvsrPRkNj2fieg7_St9P0NFC3SgZbJKS6xU,429 +werkzeug/wrappers/auth.py,sha256=rZPCzGxHk9R55PRkmS90kRywUVjjuMWzCGtH68qCq8U,856 +werkzeug/wrappers/base_request.py,sha256=saz9RyNQkvI_XLPYVm29KijNHmD1YzgxDqa0qHTbgss,1174 +werkzeug/wrappers/base_response.py,sha256=q_-TaYywT5G4zA-DWDRDJhJSat2_4O7gOPob6ye4_9A,1186 +werkzeug/wrappers/common_descriptors.py,sha256=v_kWLH3mvCiSRVJ1FNw7nO3w2UJfzY57UKKB5J4zCvE,898 +werkzeug/wrappers/cors.py,sha256=c5UndlZsZvYkbPrp6Gj5iSXxw_VOJDJHskO6-jRmNyQ,846 +werkzeug/wrappers/etag.py,sha256=XHWQQs7Mdd1oWezgBIsl-bYe8ydKkRZVil2Qd01D0Mo,846 +werkzeug/wrappers/json.py,sha256=HM1btPseGeXca0vnwQN_MvZl6h-qNsFY5YBKXKXFwus,410 +werkzeug/wrappers/request.py,sha256=yZGplfC3UqNuykwLJmgywiMhmnoKEGHJOZn_A_ublcQ,24822 +werkzeug/wrappers/response.py,sha256=0n8OcQptiM2e550SALLeg7vC1uWsUbCeE1rPZFfXR78,35177 +werkzeug/wrappers/user_agent.py,sha256=Wl1-A0-1r8o7cHIZQTB55O4Ged6LpCKENaQDlOY5pXA,435 +werkzeug/wsgi.py,sha256=L7s5-Rlt7BRVEZ1m81MaenGfMDP7yL3p1Kxt9Yssqzg,33727 diff --git a/flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/WHEEL b/flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/WHEEL new file mode 100644 index 000000000..5bad85fdc --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/top_level.txt b/flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/top_level.txt new file mode 100644 index 000000000..6fe8da849 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/Werkzeug-2.0.2.dist-info/top_level.txt @@ -0,0 +1 @@ +werkzeug diff --git a/flask-app/venv/lib/python3.8/site-packages/__pycache__/ddtrace_gevent_check.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/__pycache__/ddtrace_gevent_check.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9e6a024c6dc019c106089f2d786dcc526546fc45 GIT binary patch literal 663 zcmYjP&2G~`5cb**HI<4uaX>xn5g%fw{7I1tqG~HRL=l1%q+INccam&uuUEV4w9dIN zzzZNa@)W#>ubki!;KFX4Rvl?(cRsH_f1h_Q4i5Gm6ED7gAwQAh{Ibp6a82{Zq&hT( zIusSov|G^>QI~qx&eWyoqZ9hS%?W3m8J63yd*9@#N%hziinOyr`91kayWc$OYX7^7 z44--58&CHr)skee`7^2rWqg5m0G`3Tl7MSv!3C^=Yg|@5hXxCxv!#WZ!o1R0)k0KK z1I2_>3l=PgqQSbtGYKTedI^{aQLv0?R`Cd5D8T92S@QP$)A@Lkyg!+ozDrIn-tBgX&_42!>%0G?Z9`Am;gR5JopRqiXoKU<_iH;U K%AO5S;Qa^ilfE(l literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/__pycache__/easy_install.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/__pycache__/easy_install.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..916a571a75a4b8c01b0f12b5f62663a5343bf654 GIT binary patch literal 337 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 UtqcP_!T6f?XnRw8LkwyE7sP#NWdHyG literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/__pycache__/six.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/__pycache__/six.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..97e2c3b72a395e5cb4f4208698e2016f5b100253 GIT binary patch literal 27487 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 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/__init__.py b/flask-app/venv/lib/python3.8/site-packages/attr/__init__.py new file mode 100644 index 000000000..b1ce7fe24 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attr/__init__.py @@ -0,0 +1,78 @@ +from __future__ import absolute_import, division, print_function + +import sys + +from functools import partial + +from . import converters, exceptions, filters, setters, validators +from ._cmp import cmp_using +from ._config import get_run_validators, set_run_validators +from ._funcs import asdict, assoc, astuple, evolve, has, resolve_types +from ._make import ( + NOTHING, + Attribute, + Factory, + attrib, + attrs, + fields, + fields_dict, + make_class, + validate, +) +from ._version_info import VersionInfo + + +__version__ = "21.2.0" +__version_info__ = VersionInfo._from_version_string(__version__) + +__title__ = "attrs" +__description__ = "Classes Without Boilerplate" +__url__ = "https://www.attrs.org/" +__uri__ = __url__ +__doc__ = __description__ + " <" + __uri__ + ">" + +__author__ = "Hynek Schlawack" +__email__ = "hs@ox.cx" + +__license__ = "MIT" +__copyright__ = "Copyright (c) 2015 Hynek Schlawack" + + +s = attributes = attrs +ib = attr = attrib +dataclass = partial(attrs, auto_attribs=True) # happy Easter ;) + +__all__ = [ + "Attribute", + "Factory", + "NOTHING", + "asdict", + "assoc", + "astuple", + "attr", + "attrib", + "attributes", + "attrs", + "cmp_using", + "converters", + "evolve", + "exceptions", + "fields", + "fields_dict", + "filters", + "get_run_validators", + "has", + "ib", + "make_class", + "resolve_types", + "s", + "set_run_validators", + "setters", + "validate", + "validators", +] + +if sys.version_info[:2] >= (3, 6): + from ._next_gen import define, field, frozen, mutable + + __all__.extend((define, field, frozen, mutable)) diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/__init__.pyi b/flask-app/venv/lib/python3.8/site-packages/attr/__init__.pyi new file mode 100644 index 000000000..3503b073b --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attr/__init__.pyi @@ -0,0 +1,475 @@ +import sys + +from typing import ( + Any, + Callable, + Dict, + Generic, + List, + Mapping, + Optional, + Sequence, + Tuple, + Type, + TypeVar, + Union, + overload, +) + +# `import X as X` is required to make these public +from . import converters as converters +from . import exceptions as exceptions +from . import filters as filters +from . import setters as setters +from . import validators as validators +from ._version_info import VersionInfo + + +__version__: str +__version_info__: VersionInfo +__title__: str +__description__: str +__url__: str +__uri__: str +__author__: str +__email__: str +__license__: str +__copyright__: str + +_T = TypeVar("_T") +_C = TypeVar("_C", bound=type) + +_EqOrderType = Union[bool, Callable[[Any], Any]] +_ValidatorType = Callable[[Any, Attribute[_T], _T], Any] +_ConverterType = Callable[[Any], Any] +_FilterType = Callable[[Attribute[_T], _T], bool] +_ReprType = Callable[[Any], str] +_ReprArgType = Union[bool, _ReprType] +_OnSetAttrType = Callable[[Any, Attribute[Any], Any], Any] +_OnSetAttrArgType = Union[ + _OnSetAttrType, List[_OnSetAttrType], setters._NoOpType +] +_FieldTransformer = Callable[[type, List[Attribute[Any]]], List[Attribute[Any]]] +# FIXME: in reality, if multiple validators are passed they must be in a list +# or tuple, but those are invariant and so would prevent subtypes of +# _ValidatorType from working when passed in a list or tuple. +_ValidatorArgType = Union[_ValidatorType[_T], Sequence[_ValidatorType[_T]]] + +# _make -- + +NOTHING: object + +# NOTE: Factory lies about its return type to make this possible: +# `x: List[int] # = Factory(list)` +# Work around mypy issue #4554 in the common case by using an overload. +if sys.version_info >= (3, 8): + from typing import Literal + + @overload + def Factory(factory: Callable[[], _T]) -> _T: ... + @overload + def Factory( + factory: Callable[[Any], _T], + takes_self: Literal[True], + ) -> _T: ... + @overload + def Factory( + factory: Callable[[], _T], + takes_self: Literal[False], + ) -> _T: ... +else: + @overload + def Factory(factory: Callable[[], _T]) -> _T: ... + @overload + def Factory( + factory: Union[Callable[[Any], _T], Callable[[], _T]], + takes_self: bool = ..., + ) -> _T: ... + +# Static type inference support via __dataclass_transform__ implemented as per: +# https://github.com/microsoft/pyright/blob/1.1.135/specs/dataclass_transforms.md +# This annotation must be applied to all overloads of "define" and "attrs" +# +# NOTE: This is a typing construct and does not exist at runtime. Extensions +# wrapping attrs decorators should declare a separate __dataclass_transform__ +# signature in the extension module using the specification linked above to +# provide pyright support. +def __dataclass_transform__( + *, + eq_default: bool = True, + order_default: bool = False, + kw_only_default: bool = False, + field_descriptors: Tuple[Union[type, Callable[..., Any]], ...] = (()), +) -> Callable[[_T], _T]: ... + +class Attribute(Generic[_T]): + name: str + default: Optional[_T] + validator: Optional[_ValidatorType[_T]] + repr: _ReprArgType + cmp: _EqOrderType + eq: _EqOrderType + order: _EqOrderType + hash: Optional[bool] + init: bool + converter: Optional[_ConverterType] + metadata: Dict[Any, Any] + type: Optional[Type[_T]] + kw_only: bool + on_setattr: _OnSetAttrType + + def evolve(self, **changes: Any) -> "Attribute[Any]": ... + +# NOTE: We had several choices for the annotation to use for type arg: +# 1) Type[_T] +# - Pros: Handles simple cases correctly +# - Cons: Might produce less informative errors in the case of conflicting +# TypeVars e.g. `attr.ib(default='bad', type=int)` +# 2) Callable[..., _T] +# - Pros: Better error messages than #1 for conflicting TypeVars +# - Cons: Terrible error messages for validator checks. +# e.g. attr.ib(type=int, validator=validate_str) +# -> error: Cannot infer function type argument +# 3) type (and do all of the work in the mypy plugin) +# - Pros: Simple here, and we could customize the plugin with our own errors. +# - Cons: Would need to write mypy plugin code to handle all the cases. +# We chose option #1. + +# `attr` lies about its return type to make the following possible: +# attr() -> Any +# attr(8) -> int +# attr(validator=) -> Whatever the callable expects. +# This makes this type of assignments possible: +# x: int = attr(8) +# +# This form catches explicit None or no default but with no other arguments +# returns Any. +@overload +def attrib( + default: None = ..., + validator: None = ..., + repr: _ReprArgType = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + type: None = ..., + converter: None = ..., + factory: None = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> Any: ... + +# This form catches an explicit None or no default and infers the type from the +# other arguments. +@overload +def attrib( + default: None = ..., + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + type: Optional[Type[_T]] = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> _T: ... + +# This form catches an explicit default argument. +@overload +def attrib( + default: _T, + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + type: Optional[Type[_T]] = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> _T: ... + +# This form covers type=non-Type: e.g. forward references (str), Any +@overload +def attrib( + default: Optional[_T] = ..., + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + type: object = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> Any: ... +@overload +def field( + *, + default: None = ..., + validator: None = ..., + repr: _ReprArgType = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + converter: None = ..., + factory: None = ..., + kw_only: bool = ..., + eq: Optional[bool] = ..., + order: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> Any: ... + +# This form catches an explicit None or no default and infers the type from the +# other arguments. +@overload +def field( + *, + default: None = ..., + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> _T: ... + +# This form catches an explicit default argument. +@overload +def field( + *, + default: _T, + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> _T: ... + +# This form covers type=non-Type: e.g. forward references (str), Any +@overload +def field( + *, + default: Optional[_T] = ..., + validator: Optional[_ValidatorArgType[_T]] = ..., + repr: _ReprArgType = ..., + hash: Optional[bool] = ..., + init: bool = ..., + metadata: Optional[Mapping[Any, Any]] = ..., + converter: Optional[_ConverterType] = ..., + factory: Optional[Callable[[], _T]] = ..., + kw_only: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., +) -> Any: ... +@overload +@__dataclass_transform__(order_default=True, field_descriptors=(attrib, field)) +def attrs( + maybe_cls: _C, + these: Optional[Dict[str, Any]] = ..., + repr_ns: Optional[str] = ..., + repr: bool = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + auto_detect: bool = ..., + collect_by_mro: bool = ..., + getstate_setstate: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + field_transformer: Optional[_FieldTransformer] = ..., +) -> _C: ... +@overload +@__dataclass_transform__(order_default=True, field_descriptors=(attrib, field)) +def attrs( + maybe_cls: None = ..., + these: Optional[Dict[str, Any]] = ..., + repr_ns: Optional[str] = ..., + repr: bool = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + auto_detect: bool = ..., + collect_by_mro: bool = ..., + getstate_setstate: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + field_transformer: Optional[_FieldTransformer] = ..., +) -> Callable[[_C], _C]: ... +@overload +@__dataclass_transform__(field_descriptors=(attrib, field)) +def define( + maybe_cls: _C, + *, + these: Optional[Dict[str, Any]] = ..., + repr: bool = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[bool] = ..., + order: Optional[bool] = ..., + auto_detect: bool = ..., + getstate_setstate: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + field_transformer: Optional[_FieldTransformer] = ..., +) -> _C: ... +@overload +@__dataclass_transform__(field_descriptors=(attrib, field)) +def define( + maybe_cls: None = ..., + *, + these: Optional[Dict[str, Any]] = ..., + repr: bool = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[bool] = ..., + order: Optional[bool] = ..., + auto_detect: bool = ..., + getstate_setstate: Optional[bool] = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + field_transformer: Optional[_FieldTransformer] = ..., +) -> Callable[[_C], _C]: ... + +mutable = define +frozen = define # they differ only in their defaults + +# TODO: add support for returning NamedTuple from the mypy plugin +class _Fields(Tuple[Attribute[Any], ...]): + def __getattr__(self, name: str) -> Attribute[Any]: ... + +def fields(cls: type) -> _Fields: ... +def fields_dict(cls: type) -> Dict[str, Attribute[Any]]: ... +def validate(inst: Any) -> None: ... +def resolve_types( + cls: _C, + globalns: Optional[Dict[str, Any]] = ..., + localns: Optional[Dict[str, Any]] = ..., + attribs: Optional[List[Attribute[Any]]] = ..., +) -> _C: ... + +# TODO: add support for returning a proper attrs class from the mypy plugin +# we use Any instead of _CountingAttr so that e.g. `make_class('Foo', +# [attr.ib()])` is valid +def make_class( + name: str, + attrs: Union[List[str], Tuple[str, ...], Dict[str, Any]], + bases: Tuple[type, ...] = ..., + repr_ns: Optional[str] = ..., + repr: bool = ..., + cmp: Optional[_EqOrderType] = ..., + hash: Optional[bool] = ..., + init: bool = ..., + slots: bool = ..., + frozen: bool = ..., + weakref_slot: bool = ..., + str: bool = ..., + auto_attribs: bool = ..., + kw_only: bool = ..., + cache_hash: bool = ..., + auto_exc: bool = ..., + eq: Optional[_EqOrderType] = ..., + order: Optional[_EqOrderType] = ..., + collect_by_mro: bool = ..., + on_setattr: Optional[_OnSetAttrArgType] = ..., + field_transformer: Optional[_FieldTransformer] = ..., +) -> type: ... + +# _funcs -- + +# TODO: add support for returning TypedDict from the mypy plugin +# FIXME: asdict/astuple do not honor their factory args. Waiting on one of +# these: +# https://github.com/python/mypy/issues/4236 +# https://github.com/python/typing/issues/253 +def asdict( + inst: Any, + recurse: bool = ..., + filter: Optional[_FilterType[Any]] = ..., + dict_factory: Type[Mapping[Any, Any]] = ..., + retain_collection_types: bool = ..., + value_serializer: Optional[Callable[[type, Attribute[Any], Any], Any]] = ..., +) -> Dict[str, Any]: ... + +# TODO: add support for returning NamedTuple from the mypy plugin +def astuple( + inst: Any, + recurse: bool = ..., + filter: Optional[_FilterType[Any]] = ..., + tuple_factory: Type[Sequence[Any]] = ..., + retain_collection_types: bool = ..., +) -> Tuple[Any, ...]: ... +def has(cls: type) -> bool: ... +def assoc(inst: _T, **changes: Any) -> _T: ... +def evolve(inst: _T, **changes: Any) -> _T: ... + +# _config -- + +def set_run_validators(run: bool) -> None: ... +def get_run_validators() -> bool: ... + +# aliases -- + +s = attributes = attrs +ib = attr = attrib +dataclass = attrs # Technically, partial(attrs, auto_attribs=True) ;) diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9753ec103e5bc1a5d87f94d094e04f85e9727977 GIT binary patch literal 1834 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 Ttz5~f>ee*20^5;Co7MO)g3QKk literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/_config.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/_config.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9916d7354786f5d99f6f4d3f8bce15d6407bd397 GIT binary patch literal 764 zcma)4!EV$r5Vf7`ZW^&v+H&ILTNR0JwzMi*2_XdHgwz9FE_;c*v3Hv)u_N0_Nz@bN z2lxT*{F1Mn_ytakBLpkBFw#uMlkuCG=XpP!#t7uum(Pq4gueO1zCaAU1U2ViIO4cO z6<*_ttVtED1B^J~!H%p$9k*1C{3+cq+{{ya0nX1bAC%gTZ?tF&>m-9NEajy?7-@qPgM8q_=nW6>S5 z_@0=VB^=-5kN6I;fD>pc=-&{SzEm8E}5QlL?dm)|B=M zh2-w{uK}e$I)=1kS~bSfji4K)%K2pyx){cj#xj`;7y76>Sar2{qqWlR@UICI>;i!K ziu40${zskyDJ`=iZJAEDs^mhOv_DOk8+m)PxVU+$PtRn%$Uf}ka@n6RyzJ_9mDWXl zGL$XLleVvyT~~>eSqs;_O|x9dZLyozogP7@1JBnO3<_f$k=%Rx;&~l74Z*Pp*u(wV zF!wuQ8!i8Xz#%)q+V9#h-JY5Hm@j_>YW%*T@G^;9oWVW-zQ{7|quRf%_C2^sma(#g j_<-kU7y$M_J%idKyXbkPcvFfOehd?Vc@#t>45Iia7XQGP literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/_funcs.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/_funcs.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2519ab26c1300ca18b0208526121fecef3fcd5a0 GIT binary patch literal 10071 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 TYS+4)U)`^|R?T|a`r>~9vs>?0 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/_make.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/_make.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0845cb40b0048475159c9737968c197d6fba647e GIT binary patch literal 72959 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⪙#DY%DdJD7Hd=p|yW3MqHRQcNR}Z)B@zytm0x^ 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 Sdab*+C0}`8X<(upZu}pi7x>=* literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/_next_gen.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/_next_gen.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bea4c7105bc26915b4650389bc43f6742f9f0064 GIT binary patch literal 3270 zcmb_e&2JmW72i*CxuU2K%dOMTL6Ov}qowW=w6C%H{B>A^VW~sG8h%mY>wdbcbOZ!srr_m zo81=2QI`crqYivqpElVFTYYMDS6KZ}Yp>6Qdo<)JCsc59FqOk3CU11!{Ml9#hf_jh zMn-&kmIzigym#;@C2~k5@o5;69+gsb(jE?UtDm6XBWHme5*7>wT<};D#s_o~N^(lW z2~Ruj7vLqWt(WyN6*8b9RA2cprg5A|Dmi-(bO!y2Gfqc>4?Jux%~XoANhdP#DxGOrADwyVmeTd9KjfZL<)|V2+%FCOxU|7jT$D?y zk#Wg=S*{&(nc~jSOjRk}WMOl;HVAmgJSk|L4iXV@;p3L^S%-qLwFbq6l70jt*K;E` zb33o)_1wuDxtlNL&3rj;G%njZ9o_Q8qbEC4XVM*sU9<%vyY2*f$4gnjr)7($#~DuUG)NCcxgmG2SB3b)Zj(tgN^ zAHoWt;PqawivPDOFZbGr(||#|LJ*K%&$~pI=kxdeJD1LNCl>sQ|((YK2=^%y_;5 zfobj?wKvK}M5lf3`C%%Mi^UR(CW>VgEfhe_npj1FfDwouf$IvykhqBgO%dNkaSKGZ zHTRv@pL!9H%`4dRJrv&u(bh!+KSpWdx~Q+crY2U(lv`_?A~*%FZ=X`JJxD^}k#u`@cl&-nK0DcY^W>hm`$jz8@y?Iq`}b!* z-@)g2ynVDi4#rzS48#(KTc@+}{pmE~+lv2`-1cIAF1=$OcgEAwW{DTbU%;B_9T1vs z{J-y)vztKVLCiaEg$b~K`c6mbTCf$WLzZ%&sN?=CR0@;nQajNH#;E>8FAQb?`@D*+ z_=O(hnVD%P<^%1MSDzS}abij2r_D?k4`hv*7y6&n{AZ@FJwAB6iQzG7h<>3%7-<*A zp*Cw`omm&g$J(K0X|g_Y#h-NzU)5t=7&Yzp(Dyr-3jt-%{oK6IS3!D9faUv#hO4vl;V1*pw3r>;S=7<2)k&hvZhG{aNUcs%x)>aHugEV=G zI?x-$fc60L-GTzh-ox3u4^>864D@MyTapQI!d1#3uV2@CMz85=caGYQu)*r;Td=aoy>KrQMpx^zFn zs9;Z^#`cEz0erqi=)VAU@fxaEOWmmUh5T?0<^+Gg6|7g@sa2arE=bko^7fru>#sSw zWr&|bTidwu>1WNdKEFbhjrnON;spjQoe1tgz}mEI_(=@x440*{K7jO?h-pQGSLBCB zqvK1DzpCM0n#ohO8LNDsKuNC8r1mrS*R%7;zg61ad~AQFV22 zl~AE)t1DFo-6@8RcJrkq_b;&p{cU}(y;{~ik0m~!qcNDo;I;%fAJbQNMeVBE5FkEP z04X&Ev(ok=I^v=R0~L}gt1xeqGiV`*n`8#;`OAkO;cGG}Y azh!A(8|F)Qa7Eu=Hcu?QWxr(PSN{RPhcci5 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/_version_info.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/_version_info.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..11478bd2a31776a28ca89ae2fb9a946c8a48359d GIT binary patch literal 2443 zcmb_e&2Jnv6t_JyyOYT#t7kKt2+f5q|NO;z7p6%!7-~00Hw=**lg7)d}za}z7 z=ubPCY;_pifvz|ZjyTRxj$@pYnB-pUVLL}MKM&#nc#r#8Ew9IQ#|K%MPsLMt6h|0| zKt#L-PaU4{ng0diQ#^W#cqFEey<>cg;|8C8fSQd{I0-d9b0yhPW!BSzrFm6KZ9<;* zQfXbq zZ-{te0=LBkDl4@zwXQ4&qUcx%lg)#{9q8&Y5P@RMQA{|FJ>iRhlV>=tjk)@m3xS(* zt6-#c&Qs*f40!D+if61N;5|EXM0{mrzM1|`NNeT&qFW}vfgurv_8IMurfIpeFFKm) zy+qSaQqUbiJ7r!aQgEtE>g=Uumu8~a)q8YxdHLo>LnWbDZPC;jEXCw%+)LhYh(fO`ZFKu zBOKgvN86SAUPZl(xY$Cp`v9Q6dQwz-mF3!@Hh_pCQEqEvg|4<~- z1bJGN?$wc{X-JWYL;>lG3}gaTWvMnEBo|avRf&Qd4{8zW)$3%+VEFYgRD+ZQAk+iBqGtZ!M^YOHTQZIHt01#>p2nr(Hz!4!hLiVZs zJ)o05x@_}>kXjG<&|OF$0W(DV5Y~Nk5XyN7DqLSjSd$@y@0h6T+W#4ItOGmmBM%*5 zxdB1P>P|s0va7HM0?WOT%r6k$A?^|guRr)>9I?BF>Pd)isrQ6jq^W|~j>|it{Ul5I zS(&#+6>gK_8&L7OFS%WDx3j~D?9Fhq8jZC~c(Mt* z<%dAb>_y8?eEE@GyJ6QzPsKi%mA`6MjWO|rvAZCo+_xALJcnl?TL*I&Y;j`$vk_p9 zT(lTJwq)57Te%Zn2Y8ug>?@eP2+w~No)z0}UNs?O1>_84CSoivc`vhkgRv*QBs*KF zGsepfB#DiIeAkkBOWv^LO(4dD5|(dSZrUYOF7#f><@;8^dAx186-!6 zCCHaLK;2`nIRwcue~FhKddgoYP^2?UO0tcl2no(|cV}lm&f{DDw7hI9aNT_Rr}x*A zqWq1W)0YmNJMi!fgs*fJU-h*}jkT^8>s>uIx`wLwx^G04*zB6}y%JSpt82xzZY{RE zwyIE*{{ZXEXsNp-+j_U|SAEN`?djcR-}bG?O1D8R-wZT=$*(r5UdAqI|`tfj5eT z8wPHYCbW-cW|sjksE@wlPeJ^y1SND*`d9EI5zbMHiLlL?# z0&U-B9aX{mk&{o8V>LKhiT#-6Yqz;Uf^Crxz;MJ;k58&GbuDc6&5;5=u@uDa_XU$_F<=5~%*!$um^J3ULioD&3 zE*5p9t+|cjYXL8073`HV0%5%|11Sxoe-6B8L?O}Sfa4IzJj^Q3_o?6Mkd2$phO;gw z&twE&)a@4_ave5-rZ&gMmSM#|K^rpnz!S86 z;IVd)Mn3G_o@}(Yc9X;5`sc$2t)W1=d>2EQa0S%FZ`v*IIAm6^w(E*)|oS_mr?I1mG5bkZ+& zg3^RtdYIHs$=p3$$443RKlQJZL3&7`TxHq!dcxbM+~qV1dcBgpBxEoI%UZxg02M3# zJb{!S(m|@6k&0wWX%Schu$H+9BsNGn1-GFaPMQCoFD#g)!@Q7FPUdycA>V@&TL}%p zx2)AV>+iXDPr0f9{{}m|sTiCw;wAnfM#N)u=J7LT79}e2oC?D0y?#U8lL`iG!dnQ>DNIe1RdN0j z3JXMT!{1j~uw8pmdz z*t_`lVWI6pv4a()dxm%ljM58pSLD{MIQ2&ny^Tj7$FDbxi&|a1sKXhcS_Yt0wSSGO Gb@e}MEh%OI literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/exceptions.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/exceptions.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..203fc52ec7650b53f144ce1cbb7870d7ee45ebd1 GIT binary patch literal 3262 zcmbtWO>Y}T7~b`+KNBZTlNL%r8o_~DsCLq#mQ)2bDlO_IAd>cAK%4Q-*x9mQotbrN zD?&&`Dp!602yWyr?UfT3egG%lcXn;B+XM)Vwc~we=j(aDA2pi|1KO>hexN^A4dXWi z#byJz3y=I2I%Y7_Hv+R~2BlspuzHqhd}6Q?v%WK!#qH--uMF5`Wx!=#23!GLVO79Y zjjMobtPZ%&Yw*1exWSeHFKOHWyv&+_n|ukrF9Tj-tAJNEZUSzxHNa~cuK>Ql)&Z~c zRhZvm8+%6k;_q-mBaFACF@Sa_jw21)2Dz%H&RHWR>I zc;p+DnuveTspBAW2TK#OP1ao+zN?7jYR+o6T}f+>gEw-VwBb0wBk}Ql z*>RpG)X%?E9fw6O3|oO5rZyErd18oVkhkvs9CSfPBdU0JL`8QH`3&ySoosct`{B`( z&6`iIi>(`Byy-j}hTGed_cyT(NyzQeVz{>dYd(T(F3_9UM}5s^-Z{T7Omk zTQ_I&-#xJf-dce38lr|>9lMzjv$rOWtw6U9k1Rn~Gc`Up#f^;OqNz;2TI7h5p4#_+ zC0Q`CU9pPyKaCSA;!1%N;&XGlf^6iB(r%<4L`ldp^*zm~h2UaH3!Q5kGl&}{K11^# zc;HJ^J8@229a5#8FOeJ^UVaUZJ&44aJ_S0xp8M3_&|L$1?-b}FJ~!PBAh;81&iByw zGoD_q!Y~T2&8h0(08uK}Vhq!P2n5au>CWd(Me;xST>w7s75E`OH@{0C^8rnKwc`s; z*?5nujN$h=LzaD>v%=t&Mazs($Ln8hgPt)!dh+GSOazAodarqiW!#MfRez`8TXUy9 z+n&)sZxjT8_}l`x{3xVh7%7OHY-ZJ&48F-_km>cvQ-`R;L`4o7x7U{+?ucU1XI z!&8Ca{3wA)Pt-V`CJ}uQgwnHBf2(ujpG|uMnBFNcM|^JPYoCqbhT(n`ed_1_n=yve zd4!CLNftbFPEq?crz#N~+!lOS@TZ9fss^$lKLQ`lw$}2?^WoWyKlnG!d=VJlFEB>@ z3S$cfvB|5@6;sX&V_@pitJbr#lB-vK0cosPfmBpUTj1pPsPAL#k*WUMT=k1Kp3%fg zff6^Lzvvm-NGKUmKgm?C#RGl;ik~exTNK+fQMgks$Oqps=i@t8YuU7{ZwlGimfRoc zF@Y)K#7ygsqYWRX>SUtRBGz}0;nCUOFfW)Db8`7*>2;=OxL6lX8@^*Qy$6rP!3nH0mC9U1bipGyn#u9hi Ouv)9FTK=oI;P)5x;|_2D literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/filters.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/filters.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..45430e03acbd284abca22269618ae7060790a045 GIT binary patch literal 1776 zcmbtUOK%)S5bmDY$Lx6RIKd!=N0>1EJ&$zv zY`m+@$(EAbfFHnp|I%DJ)j0_2*%n~%QhE`$^?W8emP{M515Y41DY?*aav=e9Oz`DiSmt@#s z4(q_zWiH!*?*{80liud+6Zc-0Bv~q^UZHqY2rr6-=2Cf4CcROhwd@Cq#i1Vc-B)lw z>DlGAV5~AxXzs^Jo=IIgES|(F&eC!#mvO57s7ON%`YQ@M7PM@~DincIJz93Yf-~bW z><;l~J#Y=llY21LMF(#Jcjexv zX};^9pQQWyv(I*MoaBS=2YH;|iBqKmA?{3O`Tlg8@Bv1wj2u(|_48@jIXvMhKhNdS zHU^8KG>AnUGP~ugT*TUc`Z&=2Ly?7nP)Gfh`#%!K(wl_ehXo|OvrJgi3+wfiWd|I3 zR_OxX7-Sl9>3~ThhfV5Bv4!e25Z7U@B6A5cV1*1Q4UF(_`^t^A=r`*9q`6JE|CDcm z9>QFW8m~53)GZKz6fowI^^(3M&&hKdS@>Wsoo94Tfiw2(X9N1f$Fb%jR@%FbG2HIE z#(adCxs33LkzpKKVO77I712kJJXIYo(dWP$0Ujb4 z3qD%%4|Avou5n_b>IR58JtZ&bSu5Y3lNaQybxaHh3#g>;8(RB5FtD>?b=jN*qTpa_ z8s5k*oN@(A>BMO$3dVg8uP}Gh+0Dv$#g^J@K~2$nmc(e}d#l54;tGL=_q{yvZBPu@ zq|>#wZ21A`ZwS_}rb1tfAUyk@V2x|v{{pLS2mg*%&4o9BtDP<3YFBW{9jw+nC`{e3 zODv1$@be`cdth3@Q5mn`sJ$dU(Haco`x4nTFe=ZF@i^=W6&N4F>Orqtwtb&vq3@Tj z4~-fcC-;4cERfjtB(?(Z39}@Jmx-^cZ2C!XR=snirNLF=d226sEuq6CV};;H*whu0 PuhH1Cy0mLMu5_h zV<;Ff7uc@5?y!6RC0%#wzp&HZqmwM|dI*8{L{Z}7_rCW%{ERpN+;}X@ zr6|6Ei0Kq!d?#foV~l)0Fl`&G4W_Pv`;818IN~6 zgPD2Y2z4xv_N?WHxIB80m2s4*hoj3s8;}D_Vud8`Gz+qivk$;E4({E8nR399cGr_g z-3F(~FKXlwn8I$*WJP|3Y6E}P@_SQ+r&26Lp`o^HO(3X->5@$a%S)anso+;AObcUZP25Lxoq)o%I;e|eiEKOLd*-@mfD%r;aTe^TM z=!bG%pv=~RRnxhc4;+bHODvA;f@|pFT`Ih@zs0BUUG=~b*9Td+)B)eMEdqc$T~kD+hx<+;AZ;<1lRe5NugY}|4>6R{3j2T#H$PcPy5PpsYpkpDar53zw1cA(?j Qu)4HsdplkStmo2y0hgSH$N&HU literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/validators.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/attr/__pycache__/validators.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6448de5d0d16c6e673ca932df03d287eee1f0b4e GIT binary patch literal 11422 zcmc&)&2t>bb)T7?{lH=Ykl=?%k(7oqC1PoELCTI)M4@QXq-aaAV8x=zWFuvFu-yO_ zoSj|u%#y@naWPc6(N#(L>>^Jo<}b)0C!c)waZ;5_PO6-8Nh*#L=J#ID%zj}ZnpLSR zIgOc~?$_O~-}}9fo}bUn6%G78{Po|s-&{6~f1!ur-wYmZ;tGCc8ip{chA@TYnJueo zwKCOA%dXlc)n&YFH7jh9^>VFzHQy>!3$2;zOsiNe;=L_$-fXK>E#W>d3SveS#jGfa zIWaF5HfO4HvM7#-qxiS@H1i`v923XCHpFo`zn|GRs|(_UnEl$Q9!XoBKgm_!bqt(f=R*pE2 zR_EhZZ-{q95v@*fD_Vgb@jK!IT3v`+ofhwk5?Z}EM(aIs5v?x9t~G6>p72HuPNNHt%6GN8`{ot zCR%XUgSOWVWv$ujv{e`tMDtNIXtw=mzN4CcSlj6OjS$ak-(atevK`IUZu#PVLMyUe zU(}+U8-}X69%booP&T6@Ywhs1>v`_FCqGiEtyCV}qd6gEr`8Omq8Cw#A6ssx)ATo^ zOw+GLvrRt;UB4k~?F~ExwMI-ODz@CPu_c3=l2M`Ep-H)3ROqPoqo$C--FsM%tz<_< z*}!XuL1b^a!B&G-I{dTna1&SX4hm^hO<`0mnb|k0Hi!=3v=(e%t`kT1hl1{x|17kX>R;$-5 zDVg36F7EnnOYU8CTHPR|zPsU0N1j)E&eCQ(bao*XU49I=+|X$T7;@Kbgk9G|n{ovk zZWDpXrf%40eidc8+ZvctgtWxmQKzx;XyzUo@|H)rjkdz3sZ(5EtML+QwHKfnNN*#u zsV5`|GNaC*%?m?az@Oi5EJOAlxuINsPJ->6c z_GHt)e!chotMuFKEZ7#O*gU4nYCVd zHQ^bge4v;P_~j6v(@nbZ!ep+NEk$$WdsLmLdt#L?-o+g)M(_VsJOw!v1});{)T!&% zYvnw3r9H0IqGGMqYKyK%_oZ6xD~N%9qY9wM!>jk`u1v*X27E`;P$yBmiHm1YuyR&m zk?ST{M0&_9BFfMgW1pB~y1xVeSblE}F@}Xxxy?Y5fYwtHt2@mN7XPN-44c^8o>U~n zw5?Jwna9m=3w<7|w!f*F%Us?>4UBR#+35}hW3<7skUS{T{ zi|3gU0xiYUb?Wr4a*NR&+^Ls!5Ba1c8zJ~Yv%BoT1~4H;1+XHe1BlVfD4Q$=>LNCK zNN!kUNJl=v6_7|6bLEUWhQB}$)$U1O9mhk1-VFb!=}lZg1;r?eGnQw$%*BYFj}g5P zBl@x0r`neMOx{lrVFJ)eg>KUjq)>h186f?+8C(uc=(7blCxEwOFbBNTQ$I5RbM*$g z^*$WMUp8Ubi%t2&+2ucs!~+x-Z5j9%!!nq_sF~$PjE;=9On{AYRK(P~v2kT^pblyo zMU?Mg+obZ9LsbJcWpQxk8SehGclH1Vy*AoiUgcS{-#P;;T5QGbp58QhVo7(jN?c+&zcdPtXs&f-#c^Y$Nhq}Ae(t7ArgRum z;k7!voh$l}9`13|^T_=nP$t}JGhlUnxE9_HO$r#E__`^4EeW~dcD>NKS{@!D^~tEi zit~7@+1PT1NY$0Br^MJ#gE<1Gvdh-m@FYEPq0xjwKfg=I>uz{m=9jF>?fNa6mk>3n!G$0f@sQ7Fr+`^yt%N6G?>7_P& zBA4}EDGbkVH#gqg4_)&vWbUlKpnycJX@8kktxT}CYo8@ls=4} zy77eQ+9==_8ohG^WUH)tZ3sqiqms7m6_$Rmvb$IQqFOFR`Ar#;F^w$5FFC!(QI`9w zYoHg|+wG>0HweR|h%$i;qkPOgMKOi;010QxdB#ZfJ?=)V;Re-N*x#mL>kz2-sS5+z z7;+xL3l|rgPUA?)JYp7L11vbb1vB?D_tN5j_H4;4q6Qbeu@-@I4Rjm%5vOnBdi7vx zvG!RUu)VV7-8P;fY+~DsT}wkL*kgFq+VbLl-14rDTHZKJbanv`5N@}@L62753G-V? z%q@LH-A}c;&uJm#9L4d)!4pr6ZvH@{%L5QsrUw!~#M@uu8Uzvx zit0wR9kBvyJJAcq0*NIECqv0J6ZTqx1h8{`8<;mRVvdhEIuS1l#g+W3D+BV5FQxpx zG#pp(tj1yspn2N3GLWt%sB~AB0rE?Mgt_RoJF=2CWJ0T3Vb}>)mY3;W`l><>KV~D^ zjiAzOFH3(J*pF!~->BEdf{?V6jzrQ&onkm397$WOVA`Wos(hU2SH1rK8kwYXOv4lA z7DYGAQ-mqbaCkzprS4EMro4-&`#X{^$dh5rH>e!`QT0t+e+&lxG)`Yl$G|KovQ(36 z52Q2T(3OPoxsF6kIEPJ*e^Yni+m^p0G z%PBKI9CLVp!!hS04D%1kP@KHTDXN>moD3?{-J#?~h*Y0C8CoHtLUPtWH$p^&aZ2Mi zQ&So(_aS98{Gt3q!orH<+ydCdf-=+qo*$UV!mQ0#1O4HU!C@eUO})Yd$K z2k`uY4R&=Ckb?8pl70w!+8w0&pu2u0m84O3Jgr`Fh9Px{oJ+@RBE2}A%nHgHA~9Wd z6%p>G3h1}}E9pEz7H~P7?HC#;b+s`_oyMrsAW1EHG0g)!$v>%Fi#^G~OPwxdNAp8^ z?OaMnx&)fc#e-H7r!Z_q{$ym+G*~PY>5{rj#TaHBMcr?4X>FS;nkBP=$C2M3j~gW< zA@3(zK9a8>v`B-^ALF4n&k1PbVQ$ZUVECtONa(|&de4OKOJ_5C_Fovj_2)+44(=d9 zP1y{cpeC%zJjDqMlG21N_S)Uv@~A5XS%YltYS9cPQ^{%aA|E$l->IBce~O7|cZnR{ zfzO0j=c(cyD!xMnWrM3loChQn#`sHW{Li>_(iYz9JpL8or&9jHCdcX}GM^`g`Sl9O zHV=xE$=W3(YimP*#tFrEUc+1I8nUXX@K^xGfunvq=^U9-KS6R zHo`S4HSuosL1cX`E?|tASmf*M1D=EUp*^p0o|AI09bE zvmz#!ez#Tm2nAyDN+ls1vthu4*U7k2rZs^@&h&m;!Zn^^>eQspOhScy8h#i6Um9OC z?D0!Pyd(~aGJ)ghHHW%?!=*i*h3Uvec$g$#<5VlqL)bVx!>KROgAdPw+em**P6CVk2JhoJ;3h z8F&<(Umz8bX$6}}*cTNNda8)G1ME_7Q{w{xEXv1O_Hq7_=4PQ@aX4785OH(n6}V;C zFYS!=2RgNxjwq^`kgs$xBo?8u&pxjE7}YGg!_LRp!4RKT$R`b{Q*D*;Q|JVy zn9hM`MoxTQq~U~e)Zw(FA|}EUfR95#U?L*Zy^`zfm2k(E7(cYaA2#PhafNA7P!r3V z(`fr4a-urVDu?{Wr3q+p=xpzDN@4sIXCyqLjT(|;w#V;M-2#f!smPQjHj}pcCaz!w zMFJKwBkg_2=~J_clNw>k96ckJfMQqk^vMVyq#$x4kH0g*f}$7j<-r|%J@9e-@c{c> zYcH%QAE!n+f|!mhltM$mf=(?qp4#a3UPB}IB+F~Y0? z5EhdU7iy3Wjbzc0mTav{HK>__1v%Yrh=t5O(wAl89$8;HjG4CzX7A+4N)Oh0(oo`U z3Y)w*3KQpr>6hTrQG8~Aqvjr<5>FhjiR@G2W^OGqdUMDE#oVOBelsd0kG)S;N50e; zfCrsSroC$T@WKz98%>Gt36j1PPB^=IxgdK3j60UR8|t7QVDg9ne~v#IPVZBNrCq@) z)v}NcB#n-It`n~)0yQ#)mpQBS-&PKAY1)5)yEnl#jZ)>}I7kt%S#A{#smNK^(>NTx z{tWGwXngV$3l?SZECaK!3q?im8Vs7ap`aPjt#$spINk0=q$H{EL>!+JM?WwB!u zIK=;i?K)*oabm*A#0k--$m24&;-sdQLO*<8gU^6)lk^5ykkD#ZD)x_(cLPcORw~Vq zKI2I7BR=48z4Fwk>;U{5ID#A`0X(UDAcZfVj>cb64Yn_udnk8?9v9f84JxJpRR+DrHIMY-OJXYt za)MIxCIT!%KP4EFEjciKQ>8;;*Qf=Im^xt3vRIz)^>dqpONxWOh1?48~zB`GQ6~ z982iv`av=_VM)ncvKlX`$&c<+m@nZ$<47OJ$c!X=Ao3bWf|EP`tj|?Dqfjy; zGt&@Y(Ah!z5f~X0vR9zMJw|9ui1IWlVY_q1laHjAh(?M<VzBbj0+3iAuC- zuw(-%U;!9|CfYlncATMU4&sUj11FFC|LrJWt07mfzrot*hFyhkuoSgbBm#a- zWowi*)VU`D6#W^)481B2+(-^nh$vz&5Y1^F(jVcF{o)T66rBa~sfr@i)JZKCVU7(c z4rC8q8%qLTMF`7Ms%Sw&K*7r;pC&4->Tk$y&;o;>ps?-ZR>3-DmJsEP{reAl4x)di hfOE*jH;Y$Lo+=zgxlmX{d6LT$g$3NdU8M5l{{j$|D{TM( literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/_cmp.py b/flask-app/venv/lib/python3.8/site-packages/attr/_cmp.py new file mode 100644 index 000000000..b747b603f --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attr/_cmp.py @@ -0,0 +1,152 @@ +from __future__ import absolute_import, division, print_function + +import functools + +from ._compat import new_class +from ._make import _make_ne + + +_operation_names = {"eq": "==", "lt": "<", "le": "<=", "gt": ">", "ge": ">="} + + +def cmp_using( + eq=None, + lt=None, + le=None, + gt=None, + ge=None, + require_same_type=True, + class_name="Comparable", +): + """ + Create a class that can be passed into `attr.ib`'s ``eq``, ``order``, and + ``cmp`` arguments to customize field comparison. + + The resulting class will have a full set of ordering methods if + at least one of ``{lt, le, gt, ge}`` and ``eq`` are provided. + + :param Optional[callable] eq: `callable` used to evaluate equality + of two objects. + :param Optional[callable] lt: `callable` used to evaluate whether + one object is less than another object. + :param Optional[callable] le: `callable` used to evaluate whether + one object is less than or equal to another object. + :param Optional[callable] gt: `callable` used to evaluate whether + one object is greater than another object. + :param Optional[callable] ge: `callable` used to evaluate whether + one object is greater than or equal to another object. + + :param bool require_same_type: When `True`, equality and ordering methods + will return `NotImplemented` if objects are not of the same type. + + :param Optional[str] class_name: Name of class. Defaults to 'Comparable'. + + See `comparison` for more details. + + .. versionadded:: 21.1.0 + """ + + body = { + "__slots__": ["value"], + "__init__": _make_init(), + "_requirements": [], + "_is_comparable_to": _is_comparable_to, + } + + # Add operations. + num_order_functions = 0 + has_eq_function = False + + if eq is not None: + has_eq_function = True + body["__eq__"] = _make_operator("eq", eq) + body["__ne__"] = _make_ne() + + if lt is not None: + num_order_functions += 1 + body["__lt__"] = _make_operator("lt", lt) + + if le is not None: + num_order_functions += 1 + body["__le__"] = _make_operator("le", le) + + if gt is not None: + num_order_functions += 1 + body["__gt__"] = _make_operator("gt", gt) + + if ge is not None: + num_order_functions += 1 + body["__ge__"] = _make_operator("ge", ge) + + type_ = new_class(class_name, (object,), {}, lambda ns: ns.update(body)) + + # Add same type requirement. + if require_same_type: + type_._requirements.append(_check_same_type) + + # Add total ordering if at least one operation was defined. + if 0 < num_order_functions < 4: + if not has_eq_function: + # functools.total_ordering requires __eq__ to be defined, + # so raise early error here to keep a nice stack. + raise ValueError( + "eq must be define is order to complete ordering from " + "lt, le, gt, ge." + ) + type_ = functools.total_ordering(type_) + + return type_ + + +def _make_init(): + """ + Create __init__ method. + """ + + def __init__(self, value): + """ + Initialize object with *value*. + """ + self.value = value + + return __init__ + + +def _make_operator(name, func): + """ + Create operator method. + """ + + def method(self, other): + if not self._is_comparable_to(other): + return NotImplemented + + result = func(self.value, other.value) + if result is NotImplemented: + return NotImplemented + + return result + + method.__name__ = "__%s__" % (name,) + method.__doc__ = "Return a %s b. Computed by attrs." % ( + _operation_names[name], + ) + + return method + + +def _is_comparable_to(self, other): + """ + Check whether `other` is comparable to `self`. + """ + for func in self._requirements: + if not func(self, other): + return False + return True + + +def _check_same_type(self, other): + """ + Return True if *self* and *other* are of the same type, False otherwise. + """ + return other.value.__class__ is self.value.__class__ diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/_cmp.pyi b/flask-app/venv/lib/python3.8/site-packages/attr/_cmp.pyi new file mode 100644 index 000000000..7093550f0 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attr/_cmp.pyi @@ -0,0 +1,14 @@ +from typing import Type + +from . import _CompareWithType + + +def cmp_using( + eq: Optional[_CompareWithType], + lt: Optional[_CompareWithType], + le: Optional[_CompareWithType], + gt: Optional[_CompareWithType], + ge: Optional[_CompareWithType], + require_same_type: bool, + class_name: str, +) -> Type: ... diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/_compat.py b/flask-app/venv/lib/python3.8/site-packages/attr/_compat.py new file mode 100644 index 000000000..6939f338d --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attr/_compat.py @@ -0,0 +1,242 @@ +from __future__ import absolute_import, division, print_function + +import platform +import sys +import types +import warnings + + +PY2 = sys.version_info[0] == 2 +PYPY = platform.python_implementation() == "PyPy" + + +if PYPY or sys.version_info[:2] >= (3, 6): + ordered_dict = dict +else: + from collections import OrderedDict + + ordered_dict = OrderedDict + + +if PY2: + from collections import Mapping, Sequence + + from UserDict import IterableUserDict + + # We 'bundle' isclass instead of using inspect as importing inspect is + # fairly expensive (order of 10-15 ms for a modern machine in 2016) + def isclass(klass): + return isinstance(klass, (type, types.ClassType)) + + def new_class(name, bases, kwds, exec_body): + """ + A minimal stub of types.new_class that we need for make_class. + """ + ns = {} + exec_body(ns) + + return type(name, bases, ns) + + # TYPE is used in exceptions, repr(int) is different on Python 2 and 3. + TYPE = "type" + + def iteritems(d): + return d.iteritems() + + # Python 2 is bereft of a read-only dict proxy, so we make one! + class ReadOnlyDict(IterableUserDict): + """ + Best-effort read-only dict wrapper. + """ + + def __setitem__(self, key, val): + # We gently pretend we're a Python 3 mappingproxy. + raise TypeError( + "'mappingproxy' object does not support item assignment" + ) + + def update(self, _): + # We gently pretend we're a Python 3 mappingproxy. + raise AttributeError( + "'mappingproxy' object has no attribute 'update'" + ) + + def __delitem__(self, _): + # We gently pretend we're a Python 3 mappingproxy. + raise TypeError( + "'mappingproxy' object does not support item deletion" + ) + + def clear(self): + # We gently pretend we're a Python 3 mappingproxy. + raise AttributeError( + "'mappingproxy' object has no attribute 'clear'" + ) + + def pop(self, key, default=None): + # We gently pretend we're a Python 3 mappingproxy. + raise AttributeError( + "'mappingproxy' object has no attribute 'pop'" + ) + + def popitem(self): + # We gently pretend we're a Python 3 mappingproxy. + raise AttributeError( + "'mappingproxy' object has no attribute 'popitem'" + ) + + def setdefault(self, key, default=None): + # We gently pretend we're a Python 3 mappingproxy. + raise AttributeError( + "'mappingproxy' object has no attribute 'setdefault'" + ) + + def __repr__(self): + # Override to be identical to the Python 3 version. + return "mappingproxy(" + repr(self.data) + ")" + + def metadata_proxy(d): + res = ReadOnlyDict() + res.data.update(d) # We blocked update, so we have to do it like this. + return res + + def just_warn(*args, **kw): # pragma: no cover + """ + We only warn on Python 3 because we are not aware of any concrete + consequences of not setting the cell on Python 2. + """ + + +else: # Python 3 and later. + from collections.abc import Mapping, Sequence # noqa + + def just_warn(*args, **kw): + """ + We only warn on Python 3 because we are not aware of any concrete + consequences of not setting the cell on Python 2. + """ + warnings.warn( + "Running interpreter doesn't sufficiently support code object " + "introspection. Some features like bare super() or accessing " + "__class__ will not work with slotted classes.", + RuntimeWarning, + stacklevel=2, + ) + + def isclass(klass): + return isinstance(klass, type) + + TYPE = "class" + + def iteritems(d): + return d.items() + + new_class = types.new_class + + def metadata_proxy(d): + return types.MappingProxyType(dict(d)) + + +def make_set_closure_cell(): + """Return a function of two arguments (cell, value) which sets + the value stored in the closure cell `cell` to `value`. + """ + # pypy makes this easy. (It also supports the logic below, but + # why not do the easy/fast thing?) + if PYPY: + + def set_closure_cell(cell, value): + cell.__setstate__((value,)) + + return set_closure_cell + + # Otherwise gotta do it the hard way. + + # Create a function that will set its first cellvar to `value`. + def set_first_cellvar_to(value): + x = value + return + + # This function will be eliminated as dead code, but + # not before its reference to `x` forces `x` to be + # represented as a closure cell rather than a local. + def force_x_to_be_a_cell(): # pragma: no cover + return x + + try: + # Extract the code object and make sure our assumptions about + # the closure behavior are correct. + if PY2: + co = set_first_cellvar_to.func_code + else: + co = set_first_cellvar_to.__code__ + if co.co_cellvars != ("x",) or co.co_freevars != (): + raise AssertionError # pragma: no cover + + # Convert this code object to a code object that sets the + # function's first _freevar_ (not cellvar) to the argument. + if sys.version_info >= (3, 8): + # CPython 3.8+ has an incompatible CodeType signature + # (added a posonlyargcount argument) but also added + # CodeType.replace() to do this without counting parameters. + set_first_freevar_code = co.replace( + co_cellvars=co.co_freevars, co_freevars=co.co_cellvars + ) + else: + args = [co.co_argcount] + if not PY2: + args.append(co.co_kwonlyargcount) + args.extend( + [ + co.co_nlocals, + co.co_stacksize, + co.co_flags, + co.co_code, + co.co_consts, + co.co_names, + co.co_varnames, + co.co_filename, + co.co_name, + co.co_firstlineno, + co.co_lnotab, + # These two arguments are reversed: + co.co_cellvars, + co.co_freevars, + ] + ) + set_first_freevar_code = types.CodeType(*args) + + def set_closure_cell(cell, value): + # Create a function using the set_first_freevar_code, + # whose first closure cell is `cell`. Calling it will + # change the value of that cell. + setter = types.FunctionType( + set_first_freevar_code, {}, "setter", (), (cell,) + ) + # And call it to set the cell. + setter(value) + + # Make sure it works on this interpreter: + def make_func_with_cell(): + x = None + + def func(): + return x # pragma: no cover + + return func + + if PY2: + cell = make_func_with_cell().func_closure[0] + else: + cell = make_func_with_cell().__closure__[0] + set_closure_cell(cell, 100) + if cell.cell_contents != 100: + raise AssertionError # pragma: no cover + + except Exception: + return just_warn + else: + return set_closure_cell + + +set_closure_cell = make_set_closure_cell() diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/_config.py b/flask-app/venv/lib/python3.8/site-packages/attr/_config.py new file mode 100644 index 000000000..8ec920962 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attr/_config.py @@ -0,0 +1,23 @@ +from __future__ import absolute_import, division, print_function + + +__all__ = ["set_run_validators", "get_run_validators"] + +_run_validators = True + + +def set_run_validators(run): + """ + Set whether or not validators are run. By default, they are run. + """ + if not isinstance(run, bool): + raise TypeError("'run' must be bool.") + global _run_validators + _run_validators = run + + +def get_run_validators(): + """ + Return whether or not validators are run. + """ + return _run_validators diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/_funcs.py b/flask-app/venv/lib/python3.8/site-packages/attr/_funcs.py new file mode 100644 index 000000000..fda508c5c --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attr/_funcs.py @@ -0,0 +1,395 @@ +from __future__ import absolute_import, division, print_function + +import copy + +from ._compat import iteritems +from ._make import NOTHING, _obj_setattr, fields +from .exceptions import AttrsAttributeNotFoundError + + +def asdict( + inst, + recurse=True, + filter=None, + dict_factory=dict, + retain_collection_types=False, + value_serializer=None, +): + """ + Return the ``attrs`` attribute values of *inst* as a dict. + + Optionally recurse into other ``attrs``-decorated classes. + + :param inst: Instance of an ``attrs``-decorated class. + :param bool recurse: Recurse into classes that are also + ``attrs``-decorated. + :param callable filter: A callable whose return code determines whether an + attribute or element is included (``True``) or dropped (``False``). Is + called with the `attr.Attribute` as the first argument and the + value as the second argument. + :param callable dict_factory: A callable to produce dictionaries from. For + example, to produce ordered dictionaries instead of normal Python + dictionaries, pass in ``collections.OrderedDict``. + :param bool retain_collection_types: Do not convert to ``list`` when + encountering an attribute whose type is ``tuple`` or ``set``. Only + meaningful if ``recurse`` is ``True``. + :param Optional[callable] value_serializer: A hook that is called for every + attribute or dict key/value. It receives the current instance, field + and value and must return the (updated) value. The hook is run *after* + the optional *filter* has been applied. + + :rtype: return type of *dict_factory* + + :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` + class. + + .. versionadded:: 16.0.0 *dict_factory* + .. versionadded:: 16.1.0 *retain_collection_types* + .. versionadded:: 20.3.0 *value_serializer* + """ + attrs = fields(inst.__class__) + rv = dict_factory() + for a in attrs: + v = getattr(inst, a.name) + if filter is not None and not filter(a, v): + continue + + if value_serializer is not None: + v = value_serializer(inst, a, v) + + if recurse is True: + if has(v.__class__): + rv[a.name] = asdict( + v, + True, + filter, + dict_factory, + retain_collection_types, + value_serializer, + ) + elif isinstance(v, (tuple, list, set, frozenset)): + cf = v.__class__ if retain_collection_types is True else list + rv[a.name] = cf( + [ + _asdict_anything( + i, + filter, + dict_factory, + retain_collection_types, + value_serializer, + ) + for i in v + ] + ) + elif isinstance(v, dict): + df = dict_factory + rv[a.name] = df( + ( + _asdict_anything( + kk, + filter, + df, + retain_collection_types, + value_serializer, + ), + _asdict_anything( + vv, + filter, + df, + retain_collection_types, + value_serializer, + ), + ) + for kk, vv in iteritems(v) + ) + else: + rv[a.name] = v + else: + rv[a.name] = v + return rv + + +def _asdict_anything( + val, + filter, + dict_factory, + retain_collection_types, + value_serializer, +): + """ + ``asdict`` only works on attrs instances, this works on anything. + """ + if getattr(val.__class__, "__attrs_attrs__", None) is not None: + # Attrs class. + rv = asdict( + val, + True, + filter, + dict_factory, + retain_collection_types, + value_serializer, + ) + elif isinstance(val, (tuple, list, set, frozenset)): + cf = val.__class__ if retain_collection_types is True else list + rv = cf( + [ + _asdict_anything( + i, + filter, + dict_factory, + retain_collection_types, + value_serializer, + ) + for i in val + ] + ) + elif isinstance(val, dict): + df = dict_factory + rv = df( + ( + _asdict_anything( + kk, filter, df, retain_collection_types, value_serializer + ), + _asdict_anything( + vv, filter, df, retain_collection_types, value_serializer + ), + ) + for kk, vv in iteritems(val) + ) + else: + rv = val + if value_serializer is not None: + rv = value_serializer(None, None, rv) + + return rv + + +def astuple( + inst, + recurse=True, + filter=None, + tuple_factory=tuple, + retain_collection_types=False, +): + """ + Return the ``attrs`` attribute values of *inst* as a tuple. + + Optionally recurse into other ``attrs``-decorated classes. + + :param inst: Instance of an ``attrs``-decorated class. + :param bool recurse: Recurse into classes that are also + ``attrs``-decorated. + :param callable filter: A callable whose return code determines whether an + attribute or element is included (``True``) or dropped (``False``). Is + called with the `attr.Attribute` as the first argument and the + value as the second argument. + :param callable tuple_factory: A callable to produce tuples from. For + example, to produce lists instead of tuples. + :param bool retain_collection_types: Do not convert to ``list`` + or ``dict`` when encountering an attribute which type is + ``tuple``, ``dict`` or ``set``. Only meaningful if ``recurse`` is + ``True``. + + :rtype: return type of *tuple_factory* + + :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` + class. + + .. versionadded:: 16.2.0 + """ + attrs = fields(inst.__class__) + rv = [] + retain = retain_collection_types # Very long. :/ + for a in attrs: + v = getattr(inst, a.name) + if filter is not None and not filter(a, v): + continue + if recurse is True: + if has(v.__class__): + rv.append( + astuple( + v, + recurse=True, + filter=filter, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + ) + elif isinstance(v, (tuple, list, set, frozenset)): + cf = v.__class__ if retain is True else list + rv.append( + cf( + [ + astuple( + j, + recurse=True, + filter=filter, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + if has(j.__class__) + else j + for j in v + ] + ) + ) + elif isinstance(v, dict): + df = v.__class__ if retain is True else dict + rv.append( + df( + ( + astuple( + kk, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + if has(kk.__class__) + else kk, + astuple( + vv, + tuple_factory=tuple_factory, + retain_collection_types=retain, + ) + if has(vv.__class__) + else vv, + ) + for kk, vv in iteritems(v) + ) + ) + else: + rv.append(v) + else: + rv.append(v) + + return rv if tuple_factory is list else tuple_factory(rv) + + +def has(cls): + """ + Check whether *cls* is a class with ``attrs`` attributes. + + :param type cls: Class to introspect. + :raise TypeError: If *cls* is not a class. + + :rtype: bool + """ + return getattr(cls, "__attrs_attrs__", None) is not None + + +def assoc(inst, **changes): + """ + Copy *inst* and apply *changes*. + + :param inst: Instance of a class with ``attrs`` attributes. + :param changes: Keyword changes in the new copy. + + :return: A copy of inst with *changes* incorporated. + + :raise attr.exceptions.AttrsAttributeNotFoundError: If *attr_name* couldn't + be found on *cls*. + :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` + class. + + .. deprecated:: 17.1.0 + Use `evolve` instead. + """ + import warnings + + warnings.warn( + "assoc is deprecated and will be removed after 2018/01.", + DeprecationWarning, + stacklevel=2, + ) + new = copy.copy(inst) + attrs = fields(inst.__class__) + for k, v in iteritems(changes): + a = getattr(attrs, k, NOTHING) + if a is NOTHING: + raise AttrsAttributeNotFoundError( + "{k} is not an attrs attribute on {cl}.".format( + k=k, cl=new.__class__ + ) + ) + _obj_setattr(new, k, v) + return new + + +def evolve(inst, **changes): + """ + Create a new instance, based on *inst* with *changes* applied. + + :param inst: Instance of a class with ``attrs`` attributes. + :param changes: Keyword changes in the new copy. + + :return: A copy of inst with *changes* incorporated. + + :raise TypeError: If *attr_name* couldn't be found in the class + ``__init__``. + :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` + class. + + .. versionadded:: 17.1.0 + """ + cls = inst.__class__ + attrs = fields(cls) + for a in attrs: + if not a.init: + continue + attr_name = a.name # To deal with private attributes. + init_name = attr_name if attr_name[0] != "_" else attr_name[1:] + if init_name not in changes: + changes[init_name] = getattr(inst, attr_name) + + return cls(**changes) + + +def resolve_types(cls, globalns=None, localns=None, attribs=None): + """ + Resolve any strings and forward annotations in type annotations. + + This is only required if you need concrete types in `Attribute`'s *type* + field. In other words, you don't need to resolve your types if you only + use them for static type checking. + + With no arguments, names will be looked up in the module in which the class + was created. If this is not what you want, e.g. if the name only exists + inside a method, you may pass *globalns* or *localns* to specify other + dictionaries in which to look up these names. See the docs of + `typing.get_type_hints` for more details. + + :param type cls: Class to resolve. + :param Optional[dict] globalns: Dictionary containing global variables. + :param Optional[dict] localns: Dictionary containing local variables. + :param Optional[list] attribs: List of attribs for the given class. + This is necessary when calling from inside a ``field_transformer`` + since *cls* is not an ``attrs`` class yet. + + :raise TypeError: If *cls* is not a class. + :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` + class and you didn't pass any attribs. + :raise NameError: If types cannot be resolved because of missing variables. + + :returns: *cls* so you can use this function also as a class decorator. + Please note that you have to apply it **after** `attr.s`. That means + the decorator has to come in the line **before** `attr.s`. + + .. versionadded:: 20.1.0 + .. versionadded:: 21.1.0 *attribs* + + """ + try: + # Since calling get_type_hints is expensive we cache whether we've + # done it already. + cls.__attrs_types_resolved__ + except AttributeError: + import typing + + hints = typing.get_type_hints(cls, globalns=globalns, localns=localns) + for field in fields(cls) if attribs is None else attribs: + if field.name in hints: + # Since fields have been frozen we must work around it. + _obj_setattr(field, "type", hints[field.name]) + cls.__attrs_types_resolved__ = True + + # Return the class so you can use it as a decorator too. + return cls diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/_make.py b/flask-app/venv/lib/python3.8/site-packages/attr/_make.py new file mode 100644 index 000000000..a1912b123 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attr/_make.py @@ -0,0 +1,3052 @@ +from __future__ import absolute_import, division, print_function + +import copy +import inspect +import linecache +import sys +import threading +import uuid +import warnings + +from operator import itemgetter + +from . import _config, setters +from ._compat import ( + PY2, + PYPY, + isclass, + iteritems, + metadata_proxy, + new_class, + ordered_dict, + set_closure_cell, +) +from .exceptions import ( + DefaultAlreadySetError, + FrozenInstanceError, + NotAnAttrsClassError, + PythonTooOldError, + UnannotatedAttributeError, +) + + +if not PY2: + import typing + + +# This is used at least twice, so cache it here. +_obj_setattr = object.__setattr__ +_init_converter_pat = "__attr_converter_%s" +_init_factory_pat = "__attr_factory_{}" +_tuple_property_pat = ( + " {attr_name} = _attrs_property(_attrs_itemgetter({index}))" +) +_classvar_prefixes = ( + "typing.ClassVar", + "t.ClassVar", + "ClassVar", + "typing_extensions.ClassVar", +) +# we don't use a double-underscore prefix because that triggers +# name mangling when trying to create a slot for the field +# (when slots=True) +_hash_cache_field = "_attrs_cached_hash" + +_empty_metadata_singleton = metadata_proxy({}) + +# Unique object for unequivocal getattr() defaults. +_sentinel = object() + + +class _Nothing(object): + """ + Sentinel class to indicate the lack of a value when ``None`` is ambiguous. + + ``_Nothing`` is a singleton. There is only ever one of it. + + .. versionchanged:: 21.1.0 ``bool(NOTHING)`` is now False. + """ + + _singleton = None + + def __new__(cls): + if _Nothing._singleton is None: + _Nothing._singleton = super(_Nothing, cls).__new__(cls) + return _Nothing._singleton + + def __repr__(self): + return "NOTHING" + + def __bool__(self): + return False + + def __len__(self): + return 0 # __bool__ for Python 2 + + +NOTHING = _Nothing() +""" +Sentinel to indicate the lack of a value when ``None`` is ambiguous. +""" + + +class _CacheHashWrapper(int): + """ + An integer subclass that pickles / copies as None + + This is used for non-slots classes with ``cache_hash=True``, to avoid + serializing a potentially (even likely) invalid hash value. Since ``None`` + is the default value for uncalculated hashes, whenever this is copied, + the copy's value for the hash should automatically reset. + + See GH #613 for more details. + """ + + if PY2: + # For some reason `type(None)` isn't callable in Python 2, but we don't + # actually need a constructor for None objects, we just need any + # available function that returns None. + def __reduce__(self, _none_constructor=getattr, _args=(0, "", None)): + return _none_constructor, _args + + else: + + def __reduce__(self, _none_constructor=type(None), _args=()): + return _none_constructor, _args + + +def attrib( + default=NOTHING, + validator=None, + repr=True, + cmp=None, + hash=None, + init=True, + metadata=None, + type=None, + converter=None, + factory=None, + kw_only=False, + eq=None, + order=None, + on_setattr=None, +): + """ + Create a new attribute on a class. + + .. warning:: + + Does *not* do anything unless the class is also decorated with + `attr.s`! + + :param default: A value that is used if an ``attrs``-generated ``__init__`` + is used and no value is passed while instantiating or the attribute is + excluded using ``init=False``. + + If the value is an instance of `Factory`, its callable will be + used to construct a new value (useful for mutable data types like lists + or dicts). + + If a default is not set (or set manually to `attr.NOTHING`), a value + *must* be supplied when instantiating; otherwise a `TypeError` + will be raised. + + The default can also be set using decorator notation as shown below. + + :type default: Any value + + :param callable factory: Syntactic sugar for + ``default=attr.Factory(factory)``. + + :param validator: `callable` that is called by ``attrs``-generated + ``__init__`` methods after the instance has been initialized. They + receive the initialized instance, the `Attribute`, and the + passed value. + + The return value is *not* inspected so the validator has to throw an + exception itself. + + If a `list` is passed, its items are treated as validators and must + all pass. + + Validators can be globally disabled and re-enabled using + `get_run_validators`. + + The validator can also be set using decorator notation as shown below. + + :type validator: `callable` or a `list` of `callable`\\ s. + + :param repr: Include this attribute in the generated ``__repr__`` + method. If ``True``, include the attribute; if ``False``, omit it. By + default, the built-in ``repr()`` function is used. To override how the + attribute value is formatted, pass a ``callable`` that takes a single + value and returns a string. Note that the resulting string is used + as-is, i.e. it will be used directly *instead* of calling ``repr()`` + (the default). + :type repr: a `bool` or a `callable` to use a custom function. + + :param eq: If ``True`` (default), include this attribute in the + generated ``__eq__`` and ``__ne__`` methods that check two instances + for equality. To override how the attribute value is compared, + pass a ``callable`` that takes a single value and returns the value + to be compared. + :type eq: a `bool` or a `callable`. + + :param order: If ``True`` (default), include this attributes in the + generated ``__lt__``, ``__le__``, ``__gt__`` and ``__ge__`` methods. + To override how the attribute value is ordered, + pass a ``callable`` that takes a single value and returns the value + to be ordered. + :type order: a `bool` or a `callable`. + + :param cmp: Setting *cmp* is equivalent to setting *eq* and *order* to the + same value. Must not be mixed with *eq* or *order*. + :type cmp: a `bool` or a `callable`. + + :param Optional[bool] hash: Include this attribute in the generated + ``__hash__`` method. If ``None`` (default), mirror *eq*'s value. This + is the correct behavior according the Python spec. Setting this value + to anything else than ``None`` is *discouraged*. + :param bool init: Include this attribute in the generated ``__init__`` + method. It is possible to set this to ``False`` and set a default + value. In that case this attributed is unconditionally initialized + with the specified default value or factory. + :param callable converter: `callable` that is called by + ``attrs``-generated ``__init__`` methods to convert attribute's value + to the desired format. It is given the passed-in value, and the + returned value will be used as the new value of the attribute. The + value is converted before being passed to the validator, if any. + :param metadata: An arbitrary mapping, to be used by third-party + components. See `extending_metadata`. + :param type: The type of the attribute. In Python 3.6 or greater, the + preferred method to specify the type is using a variable annotation + (see `PEP 526 `_). + This argument is provided for backward compatibility. + Regardless of the approach used, the type will be stored on + ``Attribute.type``. + + Please note that ``attrs`` doesn't do anything with this metadata by + itself. You can use it as part of your own code or for + `static type checking `. + :param kw_only: Make this attribute keyword-only (Python 3+) + in the generated ``__init__`` (if ``init`` is ``False``, this + parameter is ignored). + :param on_setattr: Allows to overwrite the *on_setattr* setting from + `attr.s`. If left `None`, the *on_setattr* value from `attr.s` is used. + Set to `attr.setters.NO_OP` to run **no** `setattr` hooks for this + attribute -- regardless of the setting in `attr.s`. + :type on_setattr: `callable`, or a list of callables, or `None`, or + `attr.setters.NO_OP` + + .. versionadded:: 15.2.0 *convert* + .. versionadded:: 16.3.0 *metadata* + .. versionchanged:: 17.1.0 *validator* can be a ``list`` now. + .. versionchanged:: 17.1.0 + *hash* is ``None`` and therefore mirrors *eq* by default. + .. versionadded:: 17.3.0 *type* + .. deprecated:: 17.4.0 *convert* + .. versionadded:: 17.4.0 *converter* as a replacement for the deprecated + *convert* to achieve consistency with other noun-based arguments. + .. versionadded:: 18.1.0 + ``factory=f`` is syntactic sugar for ``default=attr.Factory(f)``. + .. versionadded:: 18.2.0 *kw_only* + .. versionchanged:: 19.2.0 *convert* keyword argument removed. + .. versionchanged:: 19.2.0 *repr* also accepts a custom callable. + .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01. + .. versionadded:: 19.2.0 *eq* and *order* + .. versionadded:: 20.1.0 *on_setattr* + .. versionchanged:: 20.3.0 *kw_only* backported to Python 2 + .. versionchanged:: 21.1.0 + *eq*, *order*, and *cmp* also accept a custom callable + .. versionchanged:: 21.1.0 *cmp* undeprecated + """ + eq, eq_key, order, order_key = _determine_attrib_eq_order( + cmp, eq, order, True + ) + + if hash is not None and hash is not True and hash is not False: + raise TypeError( + "Invalid value for hash. Must be True, False, or None." + ) + + if factory is not None: + if default is not NOTHING: + raise ValueError( + "The `default` and `factory` arguments are mutually " + "exclusive." + ) + if not callable(factory): + raise ValueError("The `factory` argument must be a callable.") + default = Factory(factory) + + if metadata is None: + metadata = {} + + # Apply syntactic sugar by auto-wrapping. + if isinstance(on_setattr, (list, tuple)): + on_setattr = setters.pipe(*on_setattr) + + if validator and isinstance(validator, (list, tuple)): + validator = and_(*validator) + + if converter and isinstance(converter, (list, tuple)): + converter = pipe(*converter) + + return _CountingAttr( + default=default, + validator=validator, + repr=repr, + cmp=None, + hash=hash, + init=init, + converter=converter, + metadata=metadata, + type=type, + kw_only=kw_only, + eq=eq, + eq_key=eq_key, + order=order, + order_key=order_key, + on_setattr=on_setattr, + ) + + +def _compile_and_eval(script, globs, locs=None, filename=""): + """ + "Exec" the script with the given global (globs) and local (locs) variables. + """ + bytecode = compile(script, filename, "exec") + eval(bytecode, globs, locs) + + +def _make_method(name, script, filename, globs=None): + """ + Create the method with the script given and return the method object. + """ + locs = {} + if globs is None: + globs = {} + + _compile_and_eval(script, globs, locs, filename) + + # In order of debuggers like PDB being able to step through the code, + # we add a fake linecache entry. + linecache.cache[filename] = ( + len(script), + None, + script.splitlines(True), + filename, + ) + + return locs[name] + + +def _make_attr_tuple_class(cls_name, attr_names): + """ + Create a tuple subclass to hold `Attribute`s for an `attrs` class. + + The subclass is a bare tuple with properties for names. + + class MyClassAttributes(tuple): + __slots__ = () + x = property(itemgetter(0)) + """ + attr_class_name = "{}Attributes".format(cls_name) + attr_class_template = [ + "class {}(tuple):".format(attr_class_name), + " __slots__ = ()", + ] + if attr_names: + for i, attr_name in enumerate(attr_names): + attr_class_template.append( + _tuple_property_pat.format(index=i, attr_name=attr_name) + ) + else: + attr_class_template.append(" pass") + globs = {"_attrs_itemgetter": itemgetter, "_attrs_property": property} + _compile_and_eval("\n".join(attr_class_template), globs) + return globs[attr_class_name] + + +# Tuple class for extracted attributes from a class definition. +# `base_attrs` is a subset of `attrs`. +_Attributes = _make_attr_tuple_class( + "_Attributes", + [ + # all attributes to build dunder methods for + "attrs", + # attributes that have been inherited + "base_attrs", + # map inherited attributes to their originating classes + "base_attrs_map", + ], +) + + +def _is_class_var(annot): + """ + Check whether *annot* is a typing.ClassVar. + + The string comparison hack is used to avoid evaluating all string + annotations which would put attrs-based classes at a performance + disadvantage compared to plain old classes. + """ + annot = str(annot) + + # Annotation can be quoted. + if annot.startswith(("'", '"')) and annot.endswith(("'", '"')): + annot = annot[1:-1] + + return annot.startswith(_classvar_prefixes) + + +def _has_own_attribute(cls, attrib_name): + """ + Check whether *cls* defines *attrib_name* (and doesn't just inherit it). + + Requires Python 3. + """ + attr = getattr(cls, attrib_name, _sentinel) + if attr is _sentinel: + return False + + for base_cls in cls.__mro__[1:]: + a = getattr(base_cls, attrib_name, None) + if attr is a: + return False + + return True + + +def _get_annotations(cls): + """ + Get annotations for *cls*. + """ + if _has_own_attribute(cls, "__annotations__"): + return cls.__annotations__ + + return {} + + +def _counter_getter(e): + """ + Key function for sorting to avoid re-creating a lambda for every class. + """ + return e[1].counter + + +def _collect_base_attrs(cls, taken_attr_names): + """ + Collect attr.ibs from base classes of *cls*, except *taken_attr_names*. + """ + base_attrs = [] + base_attr_map = {} # A dictionary of base attrs to their classes. + + # Traverse the MRO and collect attributes. + for base_cls in reversed(cls.__mro__[1:-1]): + for a in getattr(base_cls, "__attrs_attrs__", []): + if a.inherited or a.name in taken_attr_names: + continue + + a = a.evolve(inherited=True) + base_attrs.append(a) + base_attr_map[a.name] = base_cls + + # For each name, only keep the freshest definition i.e. the furthest at the + # back. base_attr_map is fine because it gets overwritten with every new + # instance. + filtered = [] + seen = set() + for a in reversed(base_attrs): + if a.name in seen: + continue + filtered.insert(0, a) + seen.add(a.name) + + return filtered, base_attr_map + + +def _collect_base_attrs_broken(cls, taken_attr_names): + """ + Collect attr.ibs from base classes of *cls*, except *taken_attr_names*. + + N.B. *taken_attr_names* will be mutated. + + Adhere to the old incorrect behavior. + + Notably it collects from the front and considers inherited attributes which + leads to the buggy behavior reported in #428. + """ + base_attrs = [] + base_attr_map = {} # A dictionary of base attrs to their classes. + + # Traverse the MRO and collect attributes. + for base_cls in cls.__mro__[1:-1]: + for a in getattr(base_cls, "__attrs_attrs__", []): + if a.name in taken_attr_names: + continue + + a = a.evolve(inherited=True) + taken_attr_names.add(a.name) + base_attrs.append(a) + base_attr_map[a.name] = base_cls + + return base_attrs, base_attr_map + + +def _transform_attrs( + cls, these, auto_attribs, kw_only, collect_by_mro, field_transformer +): + """ + Transform all `_CountingAttr`s on a class into `Attribute`s. + + If *these* is passed, use that and don't look for them on the class. + + *collect_by_mro* is True, collect them in the correct MRO order, otherwise + use the old -- incorrect -- order. See #428. + + Return an `_Attributes`. + """ + cd = cls.__dict__ + anns = _get_annotations(cls) + + if these is not None: + ca_list = [(name, ca) for name, ca in iteritems(these)] + + if not isinstance(these, ordered_dict): + ca_list.sort(key=_counter_getter) + elif auto_attribs is True: + ca_names = { + name + for name, attr in cd.items() + if isinstance(attr, _CountingAttr) + } + ca_list = [] + annot_names = set() + for attr_name, type in anns.items(): + if _is_class_var(type): + continue + annot_names.add(attr_name) + a = cd.get(attr_name, NOTHING) + + if not isinstance(a, _CountingAttr): + if a is NOTHING: + a = attrib() + else: + a = attrib(default=a) + ca_list.append((attr_name, a)) + + unannotated = ca_names - annot_names + if len(unannotated) > 0: + raise UnannotatedAttributeError( + "The following `attr.ib`s lack a type annotation: " + + ", ".join( + sorted(unannotated, key=lambda n: cd.get(n).counter) + ) + + "." + ) + else: + ca_list = sorted( + ( + (name, attr) + for name, attr in cd.items() + if isinstance(attr, _CountingAttr) + ), + key=lambda e: e[1].counter, + ) + + own_attrs = [ + Attribute.from_counting_attr( + name=attr_name, ca=ca, type=anns.get(attr_name) + ) + for attr_name, ca in ca_list + ] + + if collect_by_mro: + base_attrs, base_attr_map = _collect_base_attrs( + cls, {a.name for a in own_attrs} + ) + else: + base_attrs, base_attr_map = _collect_base_attrs_broken( + cls, {a.name for a in own_attrs} + ) + + attr_names = [a.name for a in base_attrs + own_attrs] + + AttrsClass = _make_attr_tuple_class(cls.__name__, attr_names) + + if kw_only: + own_attrs = [a.evolve(kw_only=True) for a in own_attrs] + base_attrs = [a.evolve(kw_only=True) for a in base_attrs] + + attrs = AttrsClass(base_attrs + own_attrs) + + # Mandatory vs non-mandatory attr order only matters when they are part of + # the __init__ signature and when they aren't kw_only (which are moved to + # the end and can be mandatory or non-mandatory in any order, as they will + # be specified as keyword args anyway). Check the order of those attrs: + had_default = False + for a in (a for a in attrs if a.init is not False and a.kw_only is False): + if had_default is True and a.default is NOTHING: + raise ValueError( + "No mandatory attributes allowed after an attribute with a " + "default value or factory. Attribute in question: %r" % (a,) + ) + + if had_default is False and a.default is not NOTHING: + had_default = True + + if field_transformer is not None: + attrs = field_transformer(cls, attrs) + return _Attributes((attrs, base_attrs, base_attr_map)) + + +if PYPY: + + def _frozen_setattrs(self, name, value): + """ + Attached to frozen classes as __setattr__. + """ + if isinstance(self, BaseException) and name in ( + "__cause__", + "__context__", + ): + BaseException.__setattr__(self, name, value) + return + + raise FrozenInstanceError() + + +else: + + def _frozen_setattrs(self, name, value): + """ + Attached to frozen classes as __setattr__. + """ + raise FrozenInstanceError() + + +def _frozen_delattrs(self, name): + """ + Attached to frozen classes as __delattr__. + """ + raise FrozenInstanceError() + + +class _ClassBuilder(object): + """ + Iteratively build *one* class. + """ + + __slots__ = ( + "_attr_names", + "_attrs", + "_base_attr_map", + "_base_names", + "_cache_hash", + "_cls", + "_cls_dict", + "_delete_attribs", + "_frozen", + "_has_pre_init", + "_has_post_init", + "_is_exc", + "_on_setattr", + "_slots", + "_weakref_slot", + "_has_own_setattr", + "_has_custom_setattr", + ) + + def __init__( + self, + cls, + these, + slots, + frozen, + weakref_slot, + getstate_setstate, + auto_attribs, + kw_only, + cache_hash, + is_exc, + collect_by_mro, + on_setattr, + has_custom_setattr, + field_transformer, + ): + attrs, base_attrs, base_map = _transform_attrs( + cls, + these, + auto_attribs, + kw_only, + collect_by_mro, + field_transformer, + ) + + self._cls = cls + self._cls_dict = dict(cls.__dict__) if slots else {} + self._attrs = attrs + self._base_names = set(a.name for a in base_attrs) + self._base_attr_map = base_map + self._attr_names = tuple(a.name for a in attrs) + self._slots = slots + self._frozen = frozen + self._weakref_slot = weakref_slot + self._cache_hash = cache_hash + self._has_pre_init = bool(getattr(cls, "__attrs_pre_init__", False)) + self._has_post_init = bool(getattr(cls, "__attrs_post_init__", False)) + self._delete_attribs = not bool(these) + self._is_exc = is_exc + self._on_setattr = on_setattr + + self._has_custom_setattr = has_custom_setattr + self._has_own_setattr = False + + self._cls_dict["__attrs_attrs__"] = self._attrs + + if frozen: + self._cls_dict["__setattr__"] = _frozen_setattrs + self._cls_dict["__delattr__"] = _frozen_delattrs + + self._has_own_setattr = True + + if getstate_setstate: + ( + self._cls_dict["__getstate__"], + self._cls_dict["__setstate__"], + ) = self._make_getstate_setstate() + + def __repr__(self): + return "<_ClassBuilder(cls={cls})>".format(cls=self._cls.__name__) + + def build_class(self): + """ + Finalize class based on the accumulated configuration. + + Builder cannot be used after calling this method. + """ + if self._slots is True: + return self._create_slots_class() + else: + return self._patch_original_class() + + def _patch_original_class(self): + """ + Apply accumulated methods and return the class. + """ + cls = self._cls + base_names = self._base_names + + # Clean class of attribute definitions (`attr.ib()`s). + if self._delete_attribs: + for name in self._attr_names: + if ( + name not in base_names + and getattr(cls, name, _sentinel) is not _sentinel + ): + try: + delattr(cls, name) + except AttributeError: + # This can happen if a base class defines a class + # variable and we want to set an attribute with the + # same name by using only a type annotation. + pass + + # Attach our dunder methods. + for name, value in self._cls_dict.items(): + setattr(cls, name, value) + + # If we've inherited an attrs __setattr__ and don't write our own, + # reset it to object's. + if not self._has_own_setattr and getattr( + cls, "__attrs_own_setattr__", False + ): + cls.__attrs_own_setattr__ = False + + if not self._has_custom_setattr: + cls.__setattr__ = object.__setattr__ + + return cls + + def _create_slots_class(self): + """ + Build and return a new class with a `__slots__` attribute. + """ + cd = { + k: v + for k, v in iteritems(self._cls_dict) + if k not in tuple(self._attr_names) + ("__dict__", "__weakref__") + } + + # If our class doesn't have its own implementation of __setattr__ + # (either from the user or by us), check the bases, if one of them has + # an attrs-made __setattr__, that needs to be reset. We don't walk the + # MRO because we only care about our immediate base classes. + # XXX: This can be confused by subclassing a slotted attrs class with + # XXX: a non-attrs class and subclass the resulting class with an attrs + # XXX: class. See `test_slotted_confused` for details. For now that's + # XXX: OK with us. + if not self._has_own_setattr: + cd["__attrs_own_setattr__"] = False + + if not self._has_custom_setattr: + for base_cls in self._cls.__bases__: + if base_cls.__dict__.get("__attrs_own_setattr__", False): + cd["__setattr__"] = object.__setattr__ + break + + # Traverse the MRO to collect existing slots + # and check for an existing __weakref__. + existing_slots = dict() + weakref_inherited = False + for base_cls in self._cls.__mro__[1:-1]: + if base_cls.__dict__.get("__weakref__", None) is not None: + weakref_inherited = True + existing_slots.update( + { + name: getattr(base_cls, name) + for name in getattr(base_cls, "__slots__", []) + } + ) + + base_names = set(self._base_names) + + names = self._attr_names + if ( + self._weakref_slot + and "__weakref__" not in getattr(self._cls, "__slots__", ()) + and "__weakref__" not in names + and not weakref_inherited + ): + names += ("__weakref__",) + + # We only add the names of attributes that aren't inherited. + # Setting __slots__ to inherited attributes wastes memory. + slot_names = [name for name in names if name not in base_names] + # There are slots for attributes from current class + # that are defined in parent classes. + # As their descriptors may be overriden by a child class, + # we collect them here and update the class dict + reused_slots = { + slot: slot_descriptor + for slot, slot_descriptor in iteritems(existing_slots) + if slot in slot_names + } + slot_names = [name for name in slot_names if name not in reused_slots] + cd.update(reused_slots) + if self._cache_hash: + slot_names.append(_hash_cache_field) + cd["__slots__"] = tuple(slot_names) + + qualname = getattr(self._cls, "__qualname__", None) + if qualname is not None: + cd["__qualname__"] = qualname + + # Create new class based on old class and our methods. + cls = type(self._cls)(self._cls.__name__, self._cls.__bases__, cd) + + # The following is a fix for + # https://github.com/python-attrs/attrs/issues/102. On Python 3, + # if a method mentions `__class__` or uses the no-arg super(), the + # compiler will bake a reference to the class in the method itself + # as `method.__closure__`. Since we replace the class with a + # clone, we rewrite these references so it keeps working. + for item in cls.__dict__.values(): + if isinstance(item, (classmethod, staticmethod)): + # Class- and staticmethods hide their functions inside. + # These might need to be rewritten as well. + closure_cells = getattr(item.__func__, "__closure__", None) + elif isinstance(item, property): + # Workaround for property `super()` shortcut (PY3-only). + # There is no universal way for other descriptors. + closure_cells = getattr(item.fget, "__closure__", None) + else: + closure_cells = getattr(item, "__closure__", None) + + if not closure_cells: # Catch None or the empty list. + continue + for cell in closure_cells: + try: + match = cell.cell_contents is self._cls + except ValueError: # ValueError: Cell is empty + pass + else: + if match: + set_closure_cell(cell, cls) + + return cls + + def add_repr(self, ns): + self._cls_dict["__repr__"] = self._add_method_dunders( + _make_repr(self._attrs, ns=ns) + ) + return self + + def add_str(self): + repr = self._cls_dict.get("__repr__") + if repr is None: + raise ValueError( + "__str__ can only be generated if a __repr__ exists." + ) + + def __str__(self): + return self.__repr__() + + self._cls_dict["__str__"] = self._add_method_dunders(__str__) + return self + + def _make_getstate_setstate(self): + """ + Create custom __setstate__ and __getstate__ methods. + """ + # __weakref__ is not writable. + state_attr_names = tuple( + an for an in self._attr_names if an != "__weakref__" + ) + + def slots_getstate(self): + """ + Automatically created by attrs. + """ + return tuple(getattr(self, name) for name in state_attr_names) + + hash_caching_enabled = self._cache_hash + + def slots_setstate(self, state): + """ + Automatically created by attrs. + """ + __bound_setattr = _obj_setattr.__get__(self, Attribute) + for name, value in zip(state_attr_names, state): + __bound_setattr(name, value) + + # The hash code cache is not included when the object is + # serialized, but it still needs to be initialized to None to + # indicate that the first call to __hash__ should be a cache + # miss. + if hash_caching_enabled: + __bound_setattr(_hash_cache_field, None) + + return slots_getstate, slots_setstate + + def make_unhashable(self): + self._cls_dict["__hash__"] = None + return self + + def add_hash(self): + self._cls_dict["__hash__"] = self._add_method_dunders( + _make_hash( + self._cls, + self._attrs, + frozen=self._frozen, + cache_hash=self._cache_hash, + ) + ) + + return self + + def add_init(self): + self._cls_dict["__init__"] = self._add_method_dunders( + _make_init( + self._cls, + self._attrs, + self._has_pre_init, + self._has_post_init, + self._frozen, + self._slots, + self._cache_hash, + self._base_attr_map, + self._is_exc, + self._on_setattr is not None + and self._on_setattr is not setters.NO_OP, + attrs_init=False, + ) + ) + + return self + + def add_attrs_init(self): + self._cls_dict["__attrs_init__"] = self._add_method_dunders( + _make_init( + self._cls, + self._attrs, + self._has_pre_init, + self._has_post_init, + self._frozen, + self._slots, + self._cache_hash, + self._base_attr_map, + self._is_exc, + self._on_setattr is not None + and self._on_setattr is not setters.NO_OP, + attrs_init=True, + ) + ) + + return self + + def add_eq(self): + cd = self._cls_dict + + cd["__eq__"] = self._add_method_dunders( + _make_eq(self._cls, self._attrs) + ) + cd["__ne__"] = self._add_method_dunders(_make_ne()) + + return self + + def add_order(self): + cd = self._cls_dict + + cd["__lt__"], cd["__le__"], cd["__gt__"], cd["__ge__"] = ( + self._add_method_dunders(meth) + for meth in _make_order(self._cls, self._attrs) + ) + + return self + + def add_setattr(self): + if self._frozen: + return self + + sa_attrs = {} + for a in self._attrs: + on_setattr = a.on_setattr or self._on_setattr + if on_setattr and on_setattr is not setters.NO_OP: + sa_attrs[a.name] = a, on_setattr + + if not sa_attrs: + return self + + if self._has_custom_setattr: + # We need to write a __setattr__ but there already is one! + raise ValueError( + "Can't combine custom __setattr__ with on_setattr hooks." + ) + + # docstring comes from _add_method_dunders + def __setattr__(self, name, val): + try: + a, hook = sa_attrs[name] + except KeyError: + nval = val + else: + nval = hook(self, a, val) + + _obj_setattr(self, name, nval) + + self._cls_dict["__attrs_own_setattr__"] = True + self._cls_dict["__setattr__"] = self._add_method_dunders(__setattr__) + self._has_own_setattr = True + + return self + + def _add_method_dunders(self, method): + """ + Add __module__ and __qualname__ to a *method* if possible. + """ + try: + method.__module__ = self._cls.__module__ + except AttributeError: + pass + + try: + method.__qualname__ = ".".join( + (self._cls.__qualname__, method.__name__) + ) + except AttributeError: + pass + + try: + method.__doc__ = "Method generated by attrs for class %s." % ( + self._cls.__qualname__, + ) + except AttributeError: + pass + + return method + + +_CMP_DEPRECATION = ( + "The usage of `cmp` is deprecated and will be removed on or after " + "2021-06-01. Please use `eq` and `order` instead." +) + + +def _determine_attrs_eq_order(cmp, eq, order, default_eq): + """ + Validate the combination of *cmp*, *eq*, and *order*. Derive the effective + values of eq and order. If *eq* is None, set it to *default_eq*. + """ + if cmp is not None and any((eq is not None, order is not None)): + raise ValueError("Don't mix `cmp` with `eq' and `order`.") + + # cmp takes precedence due to bw-compatibility. + if cmp is not None: + return cmp, cmp + + # If left None, equality is set to the specified default and ordering + # mirrors equality. + if eq is None: + eq = default_eq + + if order is None: + order = eq + + if eq is False and order is True: + raise ValueError("`order` can only be True if `eq` is True too.") + + return eq, order + + +def _determine_attrib_eq_order(cmp, eq, order, default_eq): + """ + Validate the combination of *cmp*, *eq*, and *order*. Derive the effective + values of eq and order. If *eq* is None, set it to *default_eq*. + """ + if cmp is not None and any((eq is not None, order is not None)): + raise ValueError("Don't mix `cmp` with `eq' and `order`.") + + def decide_callable_or_boolean(value): + """ + Decide whether a key function is used. + """ + if callable(value): + value, key = True, value + else: + key = None + return value, key + + # cmp takes precedence due to bw-compatibility. + if cmp is not None: + cmp, cmp_key = decide_callable_or_boolean(cmp) + return cmp, cmp_key, cmp, cmp_key + + # If left None, equality is set to the specified default and ordering + # mirrors equality. + if eq is None: + eq, eq_key = default_eq, None + else: + eq, eq_key = decide_callable_or_boolean(eq) + + if order is None: + order, order_key = eq, eq_key + else: + order, order_key = decide_callable_or_boolean(order) + + if eq is False and order is True: + raise ValueError("`order` can only be True if `eq` is True too.") + + return eq, eq_key, order, order_key + + +def _determine_whether_to_implement( + cls, flag, auto_detect, dunders, default=True +): + """ + Check whether we should implement a set of methods for *cls*. + + *flag* is the argument passed into @attr.s like 'init', *auto_detect* the + same as passed into @attr.s and *dunders* is a tuple of attribute names + whose presence signal that the user has implemented it themselves. + + Return *default* if no reason for either for or against is found. + + auto_detect must be False on Python 2. + """ + if flag is True or flag is False: + return flag + + if flag is None and auto_detect is False: + return default + + # Logically, flag is None and auto_detect is True here. + for dunder in dunders: + if _has_own_attribute(cls, dunder): + return False + + return default + + +def attrs( + maybe_cls=None, + these=None, + repr_ns=None, + repr=None, + cmp=None, + hash=None, + init=None, + slots=False, + frozen=False, + weakref_slot=True, + str=False, + auto_attribs=False, + kw_only=False, + cache_hash=False, + auto_exc=False, + eq=None, + order=None, + auto_detect=False, + collect_by_mro=False, + getstate_setstate=None, + on_setattr=None, + field_transformer=None, +): + r""" + A class decorator that adds `dunder + `_\ -methods according to the + specified attributes using `attr.ib` or the *these* argument. + + :param these: A dictionary of name to `attr.ib` mappings. This is + useful to avoid the definition of your attributes within the class body + because you can't (e.g. if you want to add ``__repr__`` methods to + Django models) or don't want to. + + If *these* is not ``None``, ``attrs`` will *not* search the class body + for attributes and will *not* remove any attributes from it. + + If *these* is an ordered dict (`dict` on Python 3.6+, + `collections.OrderedDict` otherwise), the order is deduced from + the order of the attributes inside *these*. Otherwise the order + of the definition of the attributes is used. + + :type these: `dict` of `str` to `attr.ib` + + :param str repr_ns: When using nested classes, there's no way in Python 2 + to automatically detect that. Therefore it's possible to set the + namespace explicitly for a more meaningful ``repr`` output. + :param bool auto_detect: Instead of setting the *init*, *repr*, *eq*, + *order*, and *hash* arguments explicitly, assume they are set to + ``True`` **unless any** of the involved methods for one of the + arguments is implemented in the *current* class (i.e. it is *not* + inherited from some base class). + + So for example by implementing ``__eq__`` on a class yourself, + ``attrs`` will deduce ``eq=False`` and will create *neither* + ``__eq__`` *nor* ``__ne__`` (but Python classes come with a sensible + ``__ne__`` by default, so it *should* be enough to only implement + ``__eq__`` in most cases). + + .. warning:: + + If you prevent ``attrs`` from creating the ordering methods for you + (``order=False``, e.g. by implementing ``__le__``), it becomes + *your* responsibility to make sure its ordering is sound. The best + way is to use the `functools.total_ordering` decorator. + + + Passing ``True`` or ``False`` to *init*, *repr*, *eq*, *order*, + *cmp*, or *hash* overrides whatever *auto_detect* would determine. + + *auto_detect* requires Python 3. Setting it ``True`` on Python 2 raises + a `PythonTooOldError`. + + :param bool repr: Create a ``__repr__`` method with a human readable + representation of ``attrs`` attributes.. + :param bool str: Create a ``__str__`` method that is identical to + ``__repr__``. This is usually not necessary except for + `Exception`\ s. + :param Optional[bool] eq: If ``True`` or ``None`` (default), add ``__eq__`` + and ``__ne__`` methods that check two instances for equality. + + They compare the instances as if they were tuples of their ``attrs`` + attributes if and only if the types of both classes are *identical*! + :param Optional[bool] order: If ``True``, add ``__lt__``, ``__le__``, + ``__gt__``, and ``__ge__`` methods that behave like *eq* above and + allow instances to be ordered. If ``None`` (default) mirror value of + *eq*. + :param Optional[bool] cmp: Setting *cmp* is equivalent to setting *eq* + and *order* to the same value. Must not be mixed with *eq* or *order*. + :param Optional[bool] hash: If ``None`` (default), the ``__hash__`` method + is generated according how *eq* and *frozen* are set. + + 1. If *both* are True, ``attrs`` will generate a ``__hash__`` for you. + 2. If *eq* is True and *frozen* is False, ``__hash__`` will be set to + None, marking it unhashable (which it is). + 3. If *eq* is False, ``__hash__`` will be left untouched meaning the + ``__hash__`` method of the base class will be used (if base class is + ``object``, this means it will fall back to id-based hashing.). + + Although not recommended, you can decide for yourself and force + ``attrs`` to create one (e.g. if the class is immutable even though you + didn't freeze it programmatically) by passing ``True`` or not. Both of + these cases are rather special and should be used carefully. + + See our documentation on `hashing`, Python's documentation on + `object.__hash__`, and the `GitHub issue that led to the default \ + behavior `_ for more + details. + :param bool init: Create a ``__init__`` method that initializes the + ``attrs`` attributes. Leading underscores are stripped for the argument + name. If a ``__attrs_pre_init__`` method exists on the class, it will + be called before the class is initialized. If a ``__attrs_post_init__`` + method exists on the class, it will be called after the class is fully + initialized. + + If ``init`` is ``False``, an ``__attrs_init__`` method will be + injected instead. This allows you to define a custom ``__init__`` + method that can do pre-init work such as ``super().__init__()``, + and then call ``__attrs_init__()`` and ``__attrs_post_init__()``. + :param bool slots: Create a `slotted class ` that's more + memory-efficient. Slotted classes are generally superior to the default + dict classes, but have some gotchas you should know about, so we + encourage you to read the `glossary entry `. + :param bool frozen: Make instances immutable after initialization. If + someone attempts to modify a frozen instance, + `attr.exceptions.FrozenInstanceError` is raised. + + .. note:: + + 1. This is achieved by installing a custom ``__setattr__`` method + on your class, so you can't implement your own. + + 2. True immutability is impossible in Python. + + 3. This *does* have a minor a runtime performance `impact + ` when initializing new instances. In other words: + ``__init__`` is slightly slower with ``frozen=True``. + + 4. If a class is frozen, you cannot modify ``self`` in + ``__attrs_post_init__`` or a self-written ``__init__``. You can + circumvent that limitation by using + ``object.__setattr__(self, "attribute_name", value)``. + + 5. Subclasses of a frozen class are frozen too. + + :param bool weakref_slot: Make instances weak-referenceable. This has no + effect unless ``slots`` is also enabled. + :param bool auto_attribs: If ``True``, collect `PEP 526`_-annotated + attributes (Python 3.6 and later only) from the class body. + + In this case, you **must** annotate every field. If ``attrs`` + encounters a field that is set to an `attr.ib` but lacks a type + annotation, an `attr.exceptions.UnannotatedAttributeError` is + raised. Use ``field_name: typing.Any = attr.ib(...)`` if you don't + want to set a type. + + If you assign a value to those attributes (e.g. ``x: int = 42``), that + value becomes the default value like if it were passed using + ``attr.ib(default=42)``. Passing an instance of `Factory` also + works as expected in most cases (see warning below). + + Attributes annotated as `typing.ClassVar`, and attributes that are + neither annotated nor set to an `attr.ib` are **ignored**. + + .. warning:: + For features that use the attribute name to create decorators (e.g. + `validators `), you still *must* assign `attr.ib` to + them. Otherwise Python will either not find the name or try to use + the default value to call e.g. ``validator`` on it. + + These errors can be quite confusing and probably the most common bug + report on our bug tracker. + + .. _`PEP 526`: https://www.python.org/dev/peps/pep-0526/ + :param bool kw_only: Make all attributes keyword-only (Python 3+) + in the generated ``__init__`` (if ``init`` is ``False``, this + parameter is ignored). + :param bool cache_hash: Ensure that the object's hash code is computed + only once and stored on the object. If this is set to ``True``, + hashing must be either explicitly or implicitly enabled for this + class. If the hash code is cached, avoid any reassignments of + fields involved in hash code computation or mutations of the objects + those fields point to after object creation. If such changes occur, + the behavior of the object's hash code is undefined. + :param bool auto_exc: If the class subclasses `BaseException` + (which implicitly includes any subclass of any exception), the + following happens to behave like a well-behaved Python exceptions + class: + + - the values for *eq*, *order*, and *hash* are ignored and the + instances compare and hash by the instance's ids (N.B. ``attrs`` will + *not* remove existing implementations of ``__hash__`` or the equality + methods. It just won't add own ones.), + - all attributes that are either passed into ``__init__`` or have a + default value are additionally available as a tuple in the ``args`` + attribute, + - the value of *str* is ignored leaving ``__str__`` to base classes. + :param bool collect_by_mro: Setting this to `True` fixes the way ``attrs`` + collects attributes from base classes. The default behavior is + incorrect in certain cases of multiple inheritance. It should be on by + default but is kept off for backward-compatability. + + See issue `#428 `_ for + more details. + + :param Optional[bool] getstate_setstate: + .. note:: + This is usually only interesting for slotted classes and you should + probably just set *auto_detect* to `True`. + + If `True`, ``__getstate__`` and + ``__setstate__`` are generated and attached to the class. This is + necessary for slotted classes to be pickleable. If left `None`, it's + `True` by default for slotted classes and ``False`` for dict classes. + + If *auto_detect* is `True`, and *getstate_setstate* is left `None`, + and **either** ``__getstate__`` or ``__setstate__`` is detected directly + on the class (i.e. not inherited), it is set to `False` (this is usually + what you want). + + :param on_setattr: A callable that is run whenever the user attempts to set + an attribute (either by assignment like ``i.x = 42`` or by using + `setattr` like ``setattr(i, "x", 42)``). It receives the same arguments + as validators: the instance, the attribute that is being modified, and + the new value. + + If no exception is raised, the attribute is set to the return value of + the callable. + + If a list of callables is passed, they're automatically wrapped in an + `attr.setters.pipe`. + + :param Optional[callable] field_transformer: + A function that is called with the original class object and all + fields right before ``attrs`` finalizes the class. You can use + this, e.g., to automatically add converters or validators to + fields based on their types. See `transform-fields` for more details. + + .. versionadded:: 16.0.0 *slots* + .. versionadded:: 16.1.0 *frozen* + .. versionadded:: 16.3.0 *str* + .. versionadded:: 16.3.0 Support for ``__attrs_post_init__``. + .. versionchanged:: 17.1.0 + *hash* supports ``None`` as value which is also the default now. + .. versionadded:: 17.3.0 *auto_attribs* + .. versionchanged:: 18.1.0 + If *these* is passed, no attributes are deleted from the class body. + .. versionchanged:: 18.1.0 If *these* is ordered, the order is retained. + .. versionadded:: 18.2.0 *weakref_slot* + .. deprecated:: 18.2.0 + ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now raise a + `DeprecationWarning` if the classes compared are subclasses of + each other. ``__eq`` and ``__ne__`` never tried to compared subclasses + to each other. + .. versionchanged:: 19.2.0 + ``__lt__``, ``__le__``, ``__gt__``, and ``__ge__`` now do not consider + subclasses comparable anymore. + .. versionadded:: 18.2.0 *kw_only* + .. versionadded:: 18.2.0 *cache_hash* + .. versionadded:: 19.1.0 *auto_exc* + .. deprecated:: 19.2.0 *cmp* Removal on or after 2021-06-01. + .. versionadded:: 19.2.0 *eq* and *order* + .. versionadded:: 20.1.0 *auto_detect* + .. versionadded:: 20.1.0 *collect_by_mro* + .. versionadded:: 20.1.0 *getstate_setstate* + .. versionadded:: 20.1.0 *on_setattr* + .. versionadded:: 20.3.0 *field_transformer* + .. versionchanged:: 21.1.0 + ``init=False`` injects ``__attrs_init__`` + .. versionchanged:: 21.1.0 Support for ``__attrs_pre_init__`` + .. versionchanged:: 21.1.0 *cmp* undeprecated + """ + if auto_detect and PY2: + raise PythonTooOldError( + "auto_detect only works on Python 3 and later." + ) + + eq_, order_ = _determine_attrs_eq_order(cmp, eq, order, None) + hash_ = hash # work around the lack of nonlocal + + if isinstance(on_setattr, (list, tuple)): + on_setattr = setters.pipe(*on_setattr) + + def wrap(cls): + + if getattr(cls, "__class__", None) is None: + raise TypeError("attrs only works with new-style classes.") + + is_frozen = frozen or _has_frozen_base_class(cls) + is_exc = auto_exc is True and issubclass(cls, BaseException) + has_own_setattr = auto_detect and _has_own_attribute( + cls, "__setattr__" + ) + + if has_own_setattr and is_frozen: + raise ValueError("Can't freeze a class with a custom __setattr__.") + + builder = _ClassBuilder( + cls, + these, + slots, + is_frozen, + weakref_slot, + _determine_whether_to_implement( + cls, + getstate_setstate, + auto_detect, + ("__getstate__", "__setstate__"), + default=slots, + ), + auto_attribs, + kw_only, + cache_hash, + is_exc, + collect_by_mro, + on_setattr, + has_own_setattr, + field_transformer, + ) + if _determine_whether_to_implement( + cls, repr, auto_detect, ("__repr__",) + ): + builder.add_repr(repr_ns) + if str is True: + builder.add_str() + + eq = _determine_whether_to_implement( + cls, eq_, auto_detect, ("__eq__", "__ne__") + ) + if not is_exc and eq is True: + builder.add_eq() + if not is_exc and _determine_whether_to_implement( + cls, order_, auto_detect, ("__lt__", "__le__", "__gt__", "__ge__") + ): + builder.add_order() + + builder.add_setattr() + + if ( + hash_ is None + and auto_detect is True + and _has_own_attribute(cls, "__hash__") + ): + hash = False + else: + hash = hash_ + if hash is not True and hash is not False and hash is not None: + # Can't use `hash in` because 1 == True for example. + raise TypeError( + "Invalid value for hash. Must be True, False, or None." + ) + elif hash is False or (hash is None and eq is False) or is_exc: + # Don't do anything. Should fall back to __object__'s __hash__ + # which is by id. + if cache_hash: + raise TypeError( + "Invalid value for cache_hash. To use hash caching," + " hashing must be either explicitly or implicitly " + "enabled." + ) + elif hash is True or ( + hash is None and eq is True and is_frozen is True + ): + # Build a __hash__ if told so, or if it's safe. + builder.add_hash() + else: + # Raise TypeError on attempts to hash. + if cache_hash: + raise TypeError( + "Invalid value for cache_hash. To use hash caching," + " hashing must be either explicitly or implicitly " + "enabled." + ) + builder.make_unhashable() + + if _determine_whether_to_implement( + cls, init, auto_detect, ("__init__",) + ): + builder.add_init() + else: + builder.add_attrs_init() + if cache_hash: + raise TypeError( + "Invalid value for cache_hash. To use hash caching," + " init must be True." + ) + + return builder.build_class() + + # maybe_cls's type depends on the usage of the decorator. It's a class + # if it's used as `@attrs` but ``None`` if used as `@attrs()`. + if maybe_cls is None: + return wrap + else: + return wrap(maybe_cls) + + +_attrs = attrs +""" +Internal alias so we can use it in functions that take an argument called +*attrs*. +""" + + +if PY2: + + def _has_frozen_base_class(cls): + """ + Check whether *cls* has a frozen ancestor by looking at its + __setattr__. + """ + return ( + getattr(cls.__setattr__, "__module__", None) + == _frozen_setattrs.__module__ + and cls.__setattr__.__name__ == _frozen_setattrs.__name__ + ) + + +else: + + def _has_frozen_base_class(cls): + """ + Check whether *cls* has a frozen ancestor by looking at its + __setattr__. + """ + return cls.__setattr__ == _frozen_setattrs + + +def _generate_unique_filename(cls, func_name): + """ + Create a "filename" suitable for a function being generated. + """ + unique_id = uuid.uuid4() + extra = "" + count = 1 + + while True: + unique_filename = "".format( + func_name, + cls.__module__, + getattr(cls, "__qualname__", cls.__name__), + extra, + ) + # To handle concurrency we essentially "reserve" our spot in + # the linecache with a dummy line. The caller can then + # set this value correctly. + cache_line = (1, None, (str(unique_id),), unique_filename) + if ( + linecache.cache.setdefault(unique_filename, cache_line) + == cache_line + ): + return unique_filename + + # Looks like this spot is taken. Try again. + count += 1 + extra = "-{0}".format(count) + + +def _make_hash(cls, attrs, frozen, cache_hash): + attrs = tuple( + a for a in attrs if a.hash is True or (a.hash is None and a.eq is True) + ) + + tab = " " + + unique_filename = _generate_unique_filename(cls, "hash") + type_hash = hash(unique_filename) + + hash_def = "def __hash__(self" + hash_func = "hash((" + closing_braces = "))" + if not cache_hash: + hash_def += "):" + else: + if not PY2: + hash_def += ", *" + + hash_def += ( + ", _cache_wrapper=" + + "__import__('attr._make')._make._CacheHashWrapper):" + ) + hash_func = "_cache_wrapper(" + hash_func + closing_braces += ")" + + method_lines = [hash_def] + + def append_hash_computation_lines(prefix, indent): + """ + Generate the code for actually computing the hash code. + Below this will either be returned directly or used to compute + a value which is then cached, depending on the value of cache_hash + """ + + method_lines.extend( + [ + indent + prefix + hash_func, + indent + " %d," % (type_hash,), + ] + ) + + for a in attrs: + method_lines.append(indent + " self.%s," % a.name) + + method_lines.append(indent + " " + closing_braces) + + if cache_hash: + method_lines.append(tab + "if self.%s is None:" % _hash_cache_field) + if frozen: + append_hash_computation_lines( + "object.__setattr__(self, '%s', " % _hash_cache_field, tab * 2 + ) + method_lines.append(tab * 2 + ")") # close __setattr__ + else: + append_hash_computation_lines( + "self.%s = " % _hash_cache_field, tab * 2 + ) + method_lines.append(tab + "return self.%s" % _hash_cache_field) + else: + append_hash_computation_lines("return ", tab) + + script = "\n".join(method_lines) + return _make_method("__hash__", script, unique_filename) + + +def _add_hash(cls, attrs): + """ + Add a hash method to *cls*. + """ + cls.__hash__ = _make_hash(cls, attrs, frozen=False, cache_hash=False) + return cls + + +def _make_ne(): + """ + Create __ne__ method. + """ + + def __ne__(self, other): + """ + Check equality and either forward a NotImplemented or + return the result negated. + """ + result = self.__eq__(other) + if result is NotImplemented: + return NotImplemented + + return not result + + return __ne__ + + +def _make_eq(cls, attrs): + """ + Create __eq__ method for *cls* with *attrs*. + """ + attrs = [a for a in attrs if a.eq] + + unique_filename = _generate_unique_filename(cls, "eq") + lines = [ + "def __eq__(self, other):", + " if other.__class__ is not self.__class__:", + " return NotImplemented", + ] + + # We can't just do a big self.x = other.x and... clause due to + # irregularities like nan == nan is false but (nan,) == (nan,) is true. + globs = {} + if attrs: + lines.append(" return (") + others = [" ) == ("] + for a in attrs: + if a.eq_key: + cmp_name = "_%s_key" % (a.name,) + # Add the key function to the global namespace + # of the evaluated function. + globs[cmp_name] = a.eq_key + lines.append( + " %s(self.%s)," + % ( + cmp_name, + a.name, + ) + ) + others.append( + " %s(other.%s)," + % ( + cmp_name, + a.name, + ) + ) + else: + lines.append(" self.%s," % (a.name,)) + others.append(" other.%s," % (a.name,)) + + lines += others + [" )"] + else: + lines.append(" return True") + + script = "\n".join(lines) + + return _make_method("__eq__", script, unique_filename, globs) + + +def _make_order(cls, attrs): + """ + Create ordering methods for *cls* with *attrs*. + """ + attrs = [a for a in attrs if a.order] + + def attrs_to_tuple(obj): + """ + Save us some typing. + """ + return tuple( + key(value) if key else value + for value, key in ( + (getattr(obj, a.name), a.order_key) for a in attrs + ) + ) + + def __lt__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) < attrs_to_tuple(other) + + return NotImplemented + + def __le__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) <= attrs_to_tuple(other) + + return NotImplemented + + def __gt__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) > attrs_to_tuple(other) + + return NotImplemented + + def __ge__(self, other): + """ + Automatically created by attrs. + """ + if other.__class__ is self.__class__: + return attrs_to_tuple(self) >= attrs_to_tuple(other) + + return NotImplemented + + return __lt__, __le__, __gt__, __ge__ + + +def _add_eq(cls, attrs=None): + """ + Add equality methods to *cls* with *attrs*. + """ + if attrs is None: + attrs = cls.__attrs_attrs__ + + cls.__eq__ = _make_eq(cls, attrs) + cls.__ne__ = _make_ne() + + return cls + + +_already_repring = threading.local() + + +def _make_repr(attrs, ns): + """ + Make a repr method that includes relevant *attrs*, adding *ns* to the full + name. + """ + + # Figure out which attributes to include, and which function to use to + # format them. The a.repr value can be either bool or a custom callable. + attr_names_with_reprs = tuple( + (a.name, repr if a.repr is True else a.repr) + for a in attrs + if a.repr is not False + ) + + def __repr__(self): + """ + Automatically created by attrs. + """ + try: + working_set = _already_repring.working_set + except AttributeError: + working_set = set() + _already_repring.working_set = working_set + + if id(self) in working_set: + return "..." + real_cls = self.__class__ + if ns is None: + qualname = getattr(real_cls, "__qualname__", None) + if qualname is not None: + class_name = qualname.rsplit(">.", 1)[-1] + else: + class_name = real_cls.__name__ + else: + class_name = ns + "." + real_cls.__name__ + + # Since 'self' remains on the stack (i.e.: strongly referenced) for the + # duration of this call, it's safe to depend on id(...) stability, and + # not need to track the instance and therefore worry about properties + # like weakref- or hash-ability. + working_set.add(id(self)) + try: + result = [class_name, "("] + first = True + for name, attr_repr in attr_names_with_reprs: + if first: + first = False + else: + result.append(", ") + result.extend( + (name, "=", attr_repr(getattr(self, name, NOTHING))) + ) + return "".join(result) + ")" + finally: + working_set.remove(id(self)) + + return __repr__ + + +def _add_repr(cls, ns=None, attrs=None): + """ + Add a repr method to *cls*. + """ + if attrs is None: + attrs = cls.__attrs_attrs__ + + cls.__repr__ = _make_repr(attrs, ns) + return cls + + +def fields(cls): + """ + Return the tuple of ``attrs`` attributes for a class. + + The tuple also allows accessing the fields by their names (see below for + examples). + + :param type cls: Class to introspect. + + :raise TypeError: If *cls* is not a class. + :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` + class. + + :rtype: tuple (with name accessors) of `attr.Attribute` + + .. versionchanged:: 16.2.0 Returned tuple allows accessing the fields + by name. + """ + if not isclass(cls): + raise TypeError("Passed object must be a class.") + attrs = getattr(cls, "__attrs_attrs__", None) + if attrs is None: + raise NotAnAttrsClassError( + "{cls!r} is not an attrs-decorated class.".format(cls=cls) + ) + return attrs + + +def fields_dict(cls): + """ + Return an ordered dictionary of ``attrs`` attributes for a class, whose + keys are the attribute names. + + :param type cls: Class to introspect. + + :raise TypeError: If *cls* is not a class. + :raise attr.exceptions.NotAnAttrsClassError: If *cls* is not an ``attrs`` + class. + + :rtype: an ordered dict where keys are attribute names and values are + `attr.Attribute`\\ s. This will be a `dict` if it's + naturally ordered like on Python 3.6+ or an + :class:`~collections.OrderedDict` otherwise. + + .. versionadded:: 18.1.0 + """ + if not isclass(cls): + raise TypeError("Passed object must be a class.") + attrs = getattr(cls, "__attrs_attrs__", None) + if attrs is None: + raise NotAnAttrsClassError( + "{cls!r} is not an attrs-decorated class.".format(cls=cls) + ) + return ordered_dict(((a.name, a) for a in attrs)) + + +def validate(inst): + """ + Validate all attributes on *inst* that have a validator. + + Leaves all exceptions through. + + :param inst: Instance of a class with ``attrs`` attributes. + """ + if _config._run_validators is False: + return + + for a in fields(inst.__class__): + v = a.validator + if v is not None: + v(inst, a, getattr(inst, a.name)) + + +def _is_slot_cls(cls): + return "__slots__" in cls.__dict__ + + +def _is_slot_attr(a_name, base_attr_map): + """ + Check if the attribute name comes from a slot class. + """ + return a_name in base_attr_map and _is_slot_cls(base_attr_map[a_name]) + + +def _make_init( + cls, + attrs, + pre_init, + post_init, + frozen, + slots, + cache_hash, + base_attr_map, + is_exc, + has_global_on_setattr, + attrs_init, +): + if frozen and has_global_on_setattr: + raise ValueError("Frozen classes can't use on_setattr.") + + needs_cached_setattr = cache_hash or frozen + filtered_attrs = [] + attr_dict = {} + for a in attrs: + if not a.init and a.default is NOTHING: + continue + + filtered_attrs.append(a) + attr_dict[a.name] = a + + if a.on_setattr is not None: + if frozen is True: + raise ValueError("Frozen classes can't use on_setattr.") + + needs_cached_setattr = True + elif ( + has_global_on_setattr and a.on_setattr is not setters.NO_OP + ) or _is_slot_attr(a.name, base_attr_map): + needs_cached_setattr = True + + unique_filename = _generate_unique_filename(cls, "init") + + script, globs, annotations = _attrs_to_init_script( + filtered_attrs, + frozen, + slots, + pre_init, + post_init, + cache_hash, + base_attr_map, + is_exc, + needs_cached_setattr, + has_global_on_setattr, + attrs_init, + ) + if cls.__module__ in sys.modules: + # This makes typing.get_type_hints(CLS.__init__) resolve string types. + globs.update(sys.modules[cls.__module__].__dict__) + + globs.update({"NOTHING": NOTHING, "attr_dict": attr_dict}) + + if needs_cached_setattr: + # Save the lookup overhead in __init__ if we need to circumvent + # setattr hooks. + globs["_cached_setattr"] = _obj_setattr + + init = _make_method( + "__attrs_init__" if attrs_init else "__init__", + script, + unique_filename, + globs, + ) + init.__annotations__ = annotations + + return init + + +def _setattr(attr_name, value_var, has_on_setattr): + """ + Use the cached object.setattr to set *attr_name* to *value_var*. + """ + return "_setattr('%s', %s)" % (attr_name, value_var) + + +def _setattr_with_converter(attr_name, value_var, has_on_setattr): + """ + Use the cached object.setattr to set *attr_name* to *value_var*, but run + its converter first. + """ + return "_setattr('%s', %s(%s))" % ( + attr_name, + _init_converter_pat % (attr_name,), + value_var, + ) + + +def _assign(attr_name, value, has_on_setattr): + """ + Unless *attr_name* has an on_setattr hook, use normal assignment. Otherwise + relegate to _setattr. + """ + if has_on_setattr: + return _setattr(attr_name, value, True) + + return "self.%s = %s" % (attr_name, value) + + +def _assign_with_converter(attr_name, value_var, has_on_setattr): + """ + Unless *attr_name* has an on_setattr hook, use normal assignment after + conversion. Otherwise relegate to _setattr_with_converter. + """ + if has_on_setattr: + return _setattr_with_converter(attr_name, value_var, True) + + return "self.%s = %s(%s)" % ( + attr_name, + _init_converter_pat % (attr_name,), + value_var, + ) + + +if PY2: + + def _unpack_kw_only_py2(attr_name, default=None): + """ + Unpack *attr_name* from _kw_only dict. + """ + if default is not None: + arg_default = ", %s" % default + else: + arg_default = "" + return "%s = _kw_only.pop('%s'%s)" % ( + attr_name, + attr_name, + arg_default, + ) + + def _unpack_kw_only_lines_py2(kw_only_args): + """ + Unpack all *kw_only_args* from _kw_only dict and handle errors. + + Given a list of strings "{attr_name}" and "{attr_name}={default}" + generates list of lines of code that pop attrs from _kw_only dict and + raise TypeError similar to builtin if required attr is missing or + extra key is passed. + + >>> print("\n".join(_unpack_kw_only_lines_py2(["a", "b=42"]))) + try: + a = _kw_only.pop('a') + b = _kw_only.pop('b', 42) + except KeyError as _key_error: + raise TypeError( + ... + if _kw_only: + raise TypeError( + ... + """ + lines = ["try:"] + lines.extend( + " " + _unpack_kw_only_py2(*arg.split("=")) + for arg in kw_only_args + ) + lines += """\ +except KeyError as _key_error: + raise TypeError( + '__init__() missing required keyword-only argument: %s' % _key_error + ) +if _kw_only: + raise TypeError( + '__init__() got an unexpected keyword argument %r' + % next(iter(_kw_only)) + ) +""".split( + "\n" + ) + return lines + + +def _attrs_to_init_script( + attrs, + frozen, + slots, + pre_init, + post_init, + cache_hash, + base_attr_map, + is_exc, + needs_cached_setattr, + has_global_on_setattr, + attrs_init, +): + """ + Return a script of an initializer for *attrs* and a dict of globals. + + The globals are expected by the generated script. + + If *frozen* is True, we cannot set the attributes directly so we use + a cached ``object.__setattr__``. + """ + lines = [] + if pre_init: + lines.append("self.__attrs_pre_init__()") + + if needs_cached_setattr: + lines.append( + # Circumvent the __setattr__ descriptor to save one lookup per + # assignment. + # Note _setattr will be used again below if cache_hash is True + "_setattr = _cached_setattr.__get__(self, self.__class__)" + ) + + if frozen is True: + if slots is True: + fmt_setter = _setattr + fmt_setter_with_converter = _setattr_with_converter + else: + # Dict frozen classes assign directly to __dict__. + # But only if the attribute doesn't come from an ancestor slot + # class. + # Note _inst_dict will be used again below if cache_hash is True + lines.append("_inst_dict = self.__dict__") + + def fmt_setter(attr_name, value_var, has_on_setattr): + if _is_slot_attr(attr_name, base_attr_map): + return _setattr(attr_name, value_var, has_on_setattr) + + return "_inst_dict['%s'] = %s" % (attr_name, value_var) + + def fmt_setter_with_converter( + attr_name, value_var, has_on_setattr + ): + if has_on_setattr or _is_slot_attr(attr_name, base_attr_map): + return _setattr_with_converter( + attr_name, value_var, has_on_setattr + ) + + return "_inst_dict['%s'] = %s(%s)" % ( + attr_name, + _init_converter_pat % (attr_name,), + value_var, + ) + + else: + # Not frozen. + fmt_setter = _assign + fmt_setter_with_converter = _assign_with_converter + + args = [] + kw_only_args = [] + attrs_to_validate = [] + + # This is a dictionary of names to validator and converter callables. + # Injecting this into __init__ globals lets us avoid lookups. + names_for_globals = {} + annotations = {"return": None} + + for a in attrs: + if a.validator: + attrs_to_validate.append(a) + + attr_name = a.name + has_on_setattr = a.on_setattr is not None or ( + a.on_setattr is not setters.NO_OP and has_global_on_setattr + ) + arg_name = a.name.lstrip("_") + + has_factory = isinstance(a.default, Factory) + if has_factory and a.default.takes_self: + maybe_self = "self" + else: + maybe_self = "" + + if a.init is False: + if has_factory: + init_factory_name = _init_factory_pat.format(a.name) + if a.converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, + init_factory_name + "(%s)" % (maybe_self,), + has_on_setattr, + ) + ) + conv_name = _init_converter_pat % (a.name,) + names_for_globals[conv_name] = a.converter + else: + lines.append( + fmt_setter( + attr_name, + init_factory_name + "(%s)" % (maybe_self,), + has_on_setattr, + ) + ) + names_for_globals[init_factory_name] = a.default.factory + else: + if a.converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, + "attr_dict['%s'].default" % (attr_name,), + has_on_setattr, + ) + ) + conv_name = _init_converter_pat % (a.name,) + names_for_globals[conv_name] = a.converter + else: + lines.append( + fmt_setter( + attr_name, + "attr_dict['%s'].default" % (attr_name,), + has_on_setattr, + ) + ) + elif a.default is not NOTHING and not has_factory: + arg = "%s=attr_dict['%s'].default" % (arg_name, attr_name) + if a.kw_only: + kw_only_args.append(arg) + else: + args.append(arg) + + if a.converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr + ) + ) + names_for_globals[ + _init_converter_pat % (a.name,) + ] = a.converter + else: + lines.append(fmt_setter(attr_name, arg_name, has_on_setattr)) + + elif has_factory: + arg = "%s=NOTHING" % (arg_name,) + if a.kw_only: + kw_only_args.append(arg) + else: + args.append(arg) + lines.append("if %s is not NOTHING:" % (arg_name,)) + + init_factory_name = _init_factory_pat.format(a.name) + if a.converter is not None: + lines.append( + " " + + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr + ) + ) + lines.append("else:") + lines.append( + " " + + fmt_setter_with_converter( + attr_name, + init_factory_name + "(" + maybe_self + ")", + has_on_setattr, + ) + ) + names_for_globals[ + _init_converter_pat % (a.name,) + ] = a.converter + else: + lines.append( + " " + fmt_setter(attr_name, arg_name, has_on_setattr) + ) + lines.append("else:") + lines.append( + " " + + fmt_setter( + attr_name, + init_factory_name + "(" + maybe_self + ")", + has_on_setattr, + ) + ) + names_for_globals[init_factory_name] = a.default.factory + else: + if a.kw_only: + kw_only_args.append(arg_name) + else: + args.append(arg_name) + + if a.converter is not None: + lines.append( + fmt_setter_with_converter( + attr_name, arg_name, has_on_setattr + ) + ) + names_for_globals[ + _init_converter_pat % (a.name,) + ] = a.converter + else: + lines.append(fmt_setter(attr_name, arg_name, has_on_setattr)) + + if a.init is True: + if a.type is not None and a.converter is None: + annotations[arg_name] = a.type + elif a.converter is not None and not PY2: + # Try to get the type from the converter. + sig = None + try: + sig = inspect.signature(a.converter) + except (ValueError, TypeError): # inspect failed + pass + if sig: + sig_params = list(sig.parameters.values()) + if ( + sig_params + and sig_params[0].annotation + is not inspect.Parameter.empty + ): + annotations[arg_name] = sig_params[0].annotation + + if attrs_to_validate: # we can skip this if there are no validators. + names_for_globals["_config"] = _config + lines.append("if _config._run_validators is True:") + for a in attrs_to_validate: + val_name = "__attr_validator_" + a.name + attr_name = "__attr_" + a.name + lines.append( + " %s(self, %s, self.%s)" % (val_name, attr_name, a.name) + ) + names_for_globals[val_name] = a.validator + names_for_globals[attr_name] = a + + if post_init: + lines.append("self.__attrs_post_init__()") + + # because this is set only after __attrs_post_init is called, a crash + # will result if post-init tries to access the hash code. This seemed + # preferable to setting this beforehand, in which case alteration to + # field values during post-init combined with post-init accessing the + # hash code would result in silent bugs. + if cache_hash: + if frozen: + if slots: + # if frozen and slots, then _setattr defined above + init_hash_cache = "_setattr('%s', %s)" + else: + # if frozen and not slots, then _inst_dict defined above + init_hash_cache = "_inst_dict['%s'] = %s" + else: + init_hash_cache = "self.%s = %s" + lines.append(init_hash_cache % (_hash_cache_field, "None")) + + # For exceptions we rely on BaseException.__init__ for proper + # initialization. + if is_exc: + vals = ",".join("self." + a.name for a in attrs if a.init) + + lines.append("BaseException.__init__(self, %s)" % (vals,)) + + args = ", ".join(args) + if kw_only_args: + if PY2: + lines = _unpack_kw_only_lines_py2(kw_only_args) + lines + + args += "%s**_kw_only" % (", " if args else "",) # leading comma + else: + args += "%s*, %s" % ( + ", " if args else "", # leading comma + ", ".join(kw_only_args), # kw_only args + ) + return ( + """\ +def {init_name}(self, {args}): + {lines} +""".format( + init_name=("__attrs_init__" if attrs_init else "__init__"), + args=args, + lines="\n ".join(lines) if lines else "pass", + ), + names_for_globals, + annotations, + ) + + +class Attribute(object): + """ + *Read-only* representation of an attribute. + + Instances of this class are frequently used for introspection purposes + like: + + - `fields` returns a tuple of them. + - Validators get them passed as the first argument. + - The *field transformer* hook receives a list of them. + + :attribute name: The name of the attribute. + :attribute inherited: Whether or not that attribute has been inherited from + a base class. + + Plus *all* arguments of `attr.ib` (except for ``factory`` + which is only syntactic sugar for ``default=Factory(...)``. + + .. versionadded:: 20.1.0 *inherited* + .. versionadded:: 20.1.0 *on_setattr* + .. versionchanged:: 20.2.0 *inherited* is not taken into account for + equality checks and hashing anymore. + .. versionadded:: 21.1.0 *eq_key* and *order_key* + + For the full version history of the fields, see `attr.ib`. + """ + + __slots__ = ( + "name", + "default", + "validator", + "repr", + "eq", + "eq_key", + "order", + "order_key", + "hash", + "init", + "metadata", + "type", + "converter", + "kw_only", + "inherited", + "on_setattr", + ) + + def __init__( + self, + name, + default, + validator, + repr, + cmp, # XXX: unused, remove along with other cmp code. + hash, + init, + inherited, + metadata=None, + type=None, + converter=None, + kw_only=False, + eq=None, + eq_key=None, + order=None, + order_key=None, + on_setattr=None, + ): + eq, eq_key, order, order_key = _determine_attrib_eq_order( + cmp, eq_key or eq, order_key or order, True + ) + + # Cache this descriptor here to speed things up later. + bound_setattr = _obj_setattr.__get__(self, Attribute) + + # Despite the big red warning, people *do* instantiate `Attribute` + # themselves. + bound_setattr("name", name) + bound_setattr("default", default) + bound_setattr("validator", validator) + bound_setattr("repr", repr) + bound_setattr("eq", eq) + bound_setattr("eq_key", eq_key) + bound_setattr("order", order) + bound_setattr("order_key", order_key) + bound_setattr("hash", hash) + bound_setattr("init", init) + bound_setattr("converter", converter) + bound_setattr( + "metadata", + ( + metadata_proxy(metadata) + if metadata + else _empty_metadata_singleton + ), + ) + bound_setattr("type", type) + bound_setattr("kw_only", kw_only) + bound_setattr("inherited", inherited) + bound_setattr("on_setattr", on_setattr) + + def __setattr__(self, name, value): + raise FrozenInstanceError() + + @classmethod + def from_counting_attr(cls, name, ca, type=None): + # type holds the annotated value. deal with conflicts: + if type is None: + type = ca.type + elif ca.type is not None: + raise ValueError( + "Type annotation and type argument cannot both be present" + ) + inst_dict = { + k: getattr(ca, k) + for k in Attribute.__slots__ + if k + not in ( + "name", + "validator", + "default", + "type", + "inherited", + ) # exclude methods and deprecated alias + } + return cls( + name=name, + validator=ca._validator, + default=ca._default, + type=type, + cmp=None, + inherited=False, + **inst_dict + ) + + @property + def cmp(self): + """ + Simulate the presence of a cmp attribute and warn. + """ + warnings.warn(_CMP_DEPRECATION, DeprecationWarning, stacklevel=2) + + return self.eq and self.order + + # Don't use attr.evolve since fields(Attribute) doesn't work + def evolve(self, **changes): + """ + Copy *self* and apply *changes*. + + This works similarly to `attr.evolve` but that function does not work + with ``Attribute``. + + It is mainly meant to be used for `transform-fields`. + + .. versionadded:: 20.3.0 + """ + new = copy.copy(self) + + new._setattrs(changes.items()) + + return new + + # Don't use _add_pickle since fields(Attribute) doesn't work + def __getstate__(self): + """ + Play nice with pickle. + """ + return tuple( + getattr(self, name) if name != "metadata" else dict(self.metadata) + for name in self.__slots__ + ) + + def __setstate__(self, state): + """ + Play nice with pickle. + """ + self._setattrs(zip(self.__slots__, state)) + + def _setattrs(self, name_values_pairs): + bound_setattr = _obj_setattr.__get__(self, Attribute) + for name, value in name_values_pairs: + if name != "metadata": + bound_setattr(name, value) + else: + bound_setattr( + name, + metadata_proxy(value) + if value + else _empty_metadata_singleton, + ) + + +_a = [ + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=True, + cmp=None, + eq=True, + order=False, + hash=(name != "metadata"), + init=True, + inherited=False, + ) + for name in Attribute.__slots__ +] + +Attribute = _add_hash( + _add_eq( + _add_repr(Attribute, attrs=_a), + attrs=[a for a in _a if a.name != "inherited"], + ), + attrs=[a for a in _a if a.hash and a.name != "inherited"], +) + + +class _CountingAttr(object): + """ + Intermediate representation of attributes that uses a counter to preserve + the order in which the attributes have been defined. + + *Internal* data structure of the attrs library. Running into is most + likely the result of a bug like a forgotten `@attr.s` decorator. + """ + + __slots__ = ( + "counter", + "_default", + "repr", + "eq", + "eq_key", + "order", + "order_key", + "hash", + "init", + "metadata", + "_validator", + "converter", + "type", + "kw_only", + "on_setattr", + ) + __attrs_attrs__ = tuple( + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=True, + cmp=None, + hash=True, + init=True, + kw_only=False, + eq=True, + eq_key=None, + order=False, + order_key=None, + inherited=False, + on_setattr=None, + ) + for name in ( + "counter", + "_default", + "repr", + "eq", + "order", + "hash", + "init", + "on_setattr", + ) + ) + ( + Attribute( + name="metadata", + default=None, + validator=None, + repr=True, + cmp=None, + hash=False, + init=True, + kw_only=False, + eq=True, + eq_key=None, + order=False, + order_key=None, + inherited=False, + on_setattr=None, + ), + ) + cls_counter = 0 + + def __init__( + self, + default, + validator, + repr, + cmp, + hash, + init, + converter, + metadata, + type, + kw_only, + eq, + eq_key, + order, + order_key, + on_setattr, + ): + _CountingAttr.cls_counter += 1 + self.counter = _CountingAttr.cls_counter + self._default = default + self._validator = validator + self.converter = converter + self.repr = repr + self.eq = eq + self.eq_key = eq_key + self.order = order + self.order_key = order_key + self.hash = hash + self.init = init + self.metadata = metadata + self.type = type + self.kw_only = kw_only + self.on_setattr = on_setattr + + def validator(self, meth): + """ + Decorator that adds *meth* to the list of validators. + + Returns *meth* unchanged. + + .. versionadded:: 17.1.0 + """ + if self._validator is None: + self._validator = meth + else: + self._validator = and_(self._validator, meth) + return meth + + def default(self, meth): + """ + Decorator that allows to set the default for an attribute. + + Returns *meth* unchanged. + + :raises DefaultAlreadySetError: If default has been set before. + + .. versionadded:: 17.1.0 + """ + if self._default is not NOTHING: + raise DefaultAlreadySetError() + + self._default = Factory(meth, takes_self=True) + + return meth + + +_CountingAttr = _add_eq(_add_repr(_CountingAttr)) + + +class Factory(object): + """ + Stores a factory callable. + + If passed as the default value to `attr.ib`, the factory is used to + generate a new value. + + :param callable factory: A callable that takes either none or exactly one + mandatory positional argument depending on *takes_self*. + :param bool takes_self: Pass the partially initialized instance that is + being initialized as a positional argument. + + .. versionadded:: 17.1.0 *takes_self* + """ + + __slots__ = ("factory", "takes_self") + + def __init__(self, factory, takes_self=False): + """ + `Factory` is part of the default machinery so if we want a default + value here, we have to implement it ourselves. + """ + self.factory = factory + self.takes_self = takes_self + + def __getstate__(self): + """ + Play nice with pickle. + """ + return tuple(getattr(self, name) for name in self.__slots__) + + def __setstate__(self, state): + """ + Play nice with pickle. + """ + for name, value in zip(self.__slots__, state): + setattr(self, name, value) + + +_f = [ + Attribute( + name=name, + default=NOTHING, + validator=None, + repr=True, + cmp=None, + eq=True, + order=False, + hash=True, + init=True, + inherited=False, + ) + for name in Factory.__slots__ +] + +Factory = _add_hash(_add_eq(_add_repr(Factory, attrs=_f), attrs=_f), attrs=_f) + + +def make_class(name, attrs, bases=(object,), **attributes_arguments): + """ + A quick way to create a new class called *name* with *attrs*. + + :param str name: The name for the new class. + + :param attrs: A list of names or a dictionary of mappings of names to + attributes. + + If *attrs* is a list or an ordered dict (`dict` on Python 3.6+, + `collections.OrderedDict` otherwise), the order is deduced from + the order of the names or attributes inside *attrs*. Otherwise the + order of the definition of the attributes is used. + :type attrs: `list` or `dict` + + :param tuple bases: Classes that the new class will subclass. + + :param attributes_arguments: Passed unmodified to `attr.s`. + + :return: A new class with *attrs*. + :rtype: type + + .. versionadded:: 17.1.0 *bases* + .. versionchanged:: 18.1.0 If *attrs* is ordered, the order is retained. + """ + if isinstance(attrs, dict): + cls_dict = attrs + elif isinstance(attrs, (list, tuple)): + cls_dict = dict((a, attrib()) for a in attrs) + else: + raise TypeError("attrs argument must be a dict or a list.") + + pre_init = cls_dict.pop("__attrs_pre_init__", None) + post_init = cls_dict.pop("__attrs_post_init__", None) + user_init = cls_dict.pop("__init__", None) + + body = {} + if pre_init is not None: + body["__attrs_pre_init__"] = pre_init + if post_init is not None: + body["__attrs_post_init__"] = post_init + if user_init is not None: + body["__init__"] = user_init + + type_ = new_class(name, bases, {}, lambda ns: ns.update(body)) + + # For pickling to work, the __module__ variable needs to be set to the + # frame where the class is created. Bypass this step in environments where + # sys._getframe is not defined (Jython for example) or sys._getframe is not + # defined for arguments greater than 0 (IronPython). + try: + type_.__module__ = sys._getframe(1).f_globals.get( + "__name__", "__main__" + ) + except (AttributeError, ValueError): + pass + + # We do it here for proper warnings with meaningful stacklevel. + cmp = attributes_arguments.pop("cmp", None) + ( + attributes_arguments["eq"], + attributes_arguments["order"], + ) = _determine_attrs_eq_order( + cmp, + attributes_arguments.get("eq"), + attributes_arguments.get("order"), + True, + ) + + return _attrs(these=cls_dict, **attributes_arguments)(type_) + + +# These are required by within this module so we define them here and merely +# import into .validators / .converters. + + +@attrs(slots=True, hash=True) +class _AndValidator(object): + """ + Compose many validators to a single one. + """ + + _validators = attrib() + + def __call__(self, inst, attr, value): + for v in self._validators: + v(inst, attr, value) + + +def and_(*validators): + """ + A validator that composes multiple validators into one. + + When called on a value, it runs all wrapped validators. + + :param callables validators: Arbitrary number of validators. + + .. versionadded:: 17.1.0 + """ + vals = [] + for validator in validators: + vals.extend( + validator._validators + if isinstance(validator, _AndValidator) + else [validator] + ) + + return _AndValidator(tuple(vals)) + + +def pipe(*converters): + """ + A converter that composes multiple converters into one. + + When called on a value, it runs all wrapped converters, returning the + *last* value. + + Type annotations will be inferred from the wrapped converters', if + they have any. + + :param callables converters: Arbitrary number of converters. + + .. versionadded:: 20.1.0 + """ + + def pipe_converter(val): + for converter in converters: + val = converter(val) + + return val + + if not PY2: + if not converters: + # If the converter list is empty, pipe_converter is the identity. + A = typing.TypeVar("A") + pipe_converter.__annotations__ = {"val": A, "return": A} + else: + # Get parameter type. + sig = None + try: + sig = inspect.signature(converters[0]) + except (ValueError, TypeError): # inspect failed + pass + if sig: + params = list(sig.parameters.values()) + if ( + params + and params[0].annotation is not inspect.Parameter.empty + ): + pipe_converter.__annotations__["val"] = params[ + 0 + ].annotation + # Get return type. + sig = None + try: + sig = inspect.signature(converters[-1]) + except (ValueError, TypeError): # inspect failed + pass + if sig and sig.return_annotation is not inspect.Signature().empty: + pipe_converter.__annotations__[ + "return" + ] = sig.return_annotation + + return pipe_converter diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/_next_gen.py b/flask-app/venv/lib/python3.8/site-packages/attr/_next_gen.py new file mode 100644 index 000000000..fab0af966 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attr/_next_gen.py @@ -0,0 +1,158 @@ +""" +These are Python 3.6+-only and keyword-only APIs that call `attr.s` and +`attr.ib` with different default values. +""" + +from functools import partial + +from attr.exceptions import UnannotatedAttributeError + +from . import setters +from ._make import NOTHING, _frozen_setattrs, attrib, attrs + + +def define( + maybe_cls=None, + *, + these=None, + repr=None, + hash=None, + init=None, + slots=True, + frozen=False, + weakref_slot=True, + str=False, + auto_attribs=None, + kw_only=False, + cache_hash=False, + auto_exc=True, + eq=None, + order=False, + auto_detect=True, + getstate_setstate=None, + on_setattr=None, + field_transformer=None, +): + r""" + The only behavioral differences are the handling of the *auto_attribs* + option: + + :param Optional[bool] auto_attribs: If set to `True` or `False`, it behaves + exactly like `attr.s`. If left `None`, `attr.s` will try to guess: + + 1. If any attributes are annotated and no unannotated `attr.ib`\ s + are found, it assumes *auto_attribs=True*. + 2. Otherwise it assumes *auto_attribs=False* and tries to collect + `attr.ib`\ s. + + and that mutable classes (``frozen=False``) validate on ``__setattr__``. + + .. versionadded:: 20.1.0 + """ + + def do_it(cls, auto_attribs): + return attrs( + maybe_cls=cls, + these=these, + repr=repr, + hash=hash, + init=init, + slots=slots, + frozen=frozen, + weakref_slot=weakref_slot, + str=str, + auto_attribs=auto_attribs, + kw_only=kw_only, + cache_hash=cache_hash, + auto_exc=auto_exc, + eq=eq, + order=order, + auto_detect=auto_detect, + collect_by_mro=True, + getstate_setstate=getstate_setstate, + on_setattr=on_setattr, + field_transformer=field_transformer, + ) + + def wrap(cls): + """ + Making this a wrapper ensures this code runs during class creation. + + We also ensure that frozen-ness of classes is inherited. + """ + nonlocal frozen, on_setattr + + had_on_setattr = on_setattr not in (None, setters.NO_OP) + + # By default, mutable classes validate on setattr. + if frozen is False and on_setattr is None: + on_setattr = setters.validate + + # However, if we subclass a frozen class, we inherit the immutability + # and disable on_setattr. + for base_cls in cls.__bases__: + if base_cls.__setattr__ is _frozen_setattrs: + if had_on_setattr: + raise ValueError( + "Frozen classes can't use on_setattr " + "(frozen-ness was inherited)." + ) + + on_setattr = setters.NO_OP + break + + if auto_attribs is not None: + return do_it(cls, auto_attribs) + + try: + return do_it(cls, True) + except UnannotatedAttributeError: + return do_it(cls, False) + + # maybe_cls's type depends on the usage of the decorator. It's a class + # if it's used as `@attrs` but ``None`` if used as `@attrs()`. + if maybe_cls is None: + return wrap + else: + return wrap(maybe_cls) + + +mutable = define +frozen = partial(define, frozen=True, on_setattr=None) + + +def field( + *, + default=NOTHING, + validator=None, + repr=True, + hash=None, + init=True, + metadata=None, + converter=None, + factory=None, + kw_only=False, + eq=None, + order=None, + on_setattr=None, +): + """ + Identical to `attr.ib`, except keyword-only and with some arguments + removed. + + .. versionadded:: 20.1.0 + """ + return attrib( + default=default, + validator=validator, + repr=repr, + hash=hash, + init=init, + metadata=metadata, + converter=converter, + factory=factory, + kw_only=kw_only, + eq=eq, + order=order, + on_setattr=on_setattr, + ) diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/_version_info.py b/flask-app/venv/lib/python3.8/site-packages/attr/_version_info.py new file mode 100644 index 000000000..014e78a1b --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attr/_version_info.py @@ -0,0 +1,85 @@ +from __future__ import absolute_import, division, print_function + +from functools import total_ordering + +from ._funcs import astuple +from ._make import attrib, attrs + + +@total_ordering +@attrs(eq=False, order=False, slots=True, frozen=True) +class VersionInfo(object): + """ + A version object that can be compared to tuple of length 1--4: + + >>> attr.VersionInfo(19, 1, 0, "final") <= (19, 2) + True + >>> attr.VersionInfo(19, 1, 0, "final") < (19, 1, 1) + True + >>> vi = attr.VersionInfo(19, 2, 0, "final") + >>> vi < (19, 1, 1) + False + >>> vi < (19,) + False + >>> vi == (19, 2,) + True + >>> vi == (19, 2, 1) + False + + .. versionadded:: 19.2 + """ + + year = attrib(type=int) + minor = attrib(type=int) + micro = attrib(type=int) + releaselevel = attrib(type=str) + + @classmethod + def _from_version_string(cls, s): + """ + Parse *s* and return a _VersionInfo. + """ + v = s.split(".") + if len(v) == 3: + v.append("final") + + return cls( + year=int(v[0]), minor=int(v[1]), micro=int(v[2]), releaselevel=v[3] + ) + + def _ensure_tuple(self, other): + """ + Ensure *other* is a tuple of a valid length. + + Returns a possibly transformed *other* and ourselves as a tuple of + the same length as *other*. + """ + + if self.__class__ is other.__class__: + other = astuple(other) + + if not isinstance(other, tuple): + raise NotImplementedError + + if not (1 <= len(other) <= 4): + raise NotImplementedError + + return astuple(self)[: len(other)], other + + def __eq__(self, other): + try: + us, them = self._ensure_tuple(other) + except NotImplementedError: + return NotImplemented + + return us == them + + def __lt__(self, other): + try: + us, them = self._ensure_tuple(other) + except NotImplementedError: + return NotImplemented + + # Since alphabetically "dev0" < "final" < "post1" < "post2", we don't + # have to do anything special with releaselevel for now. + return us < them diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/_version_info.pyi b/flask-app/venv/lib/python3.8/site-packages/attr/_version_info.pyi new file mode 100644 index 000000000..45ced0863 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attr/_version_info.pyi @@ -0,0 +1,9 @@ +class VersionInfo: + @property + def year(self) -> int: ... + @property + def minor(self) -> int: ... + @property + def micro(self) -> int: ... + @property + def releaselevel(self) -> str: ... diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/converters.py b/flask-app/venv/lib/python3.8/site-packages/attr/converters.py new file mode 100644 index 000000000..2777db6d0 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attr/converters.py @@ -0,0 +1,111 @@ +""" +Commonly useful converters. +""" + +from __future__ import absolute_import, division, print_function + +from ._compat import PY2 +from ._make import NOTHING, Factory, pipe + + +if not PY2: + import inspect + import typing + + +__all__ = [ + "pipe", + "optional", + "default_if_none", +] + + +def optional(converter): + """ + A converter that allows an attribute to be optional. An optional attribute + is one which can be set to ``None``. + + Type annotations will be inferred from the wrapped converter's, if it + has any. + + :param callable converter: the converter that is used for non-``None`` + values. + + .. versionadded:: 17.1.0 + """ + + def optional_converter(val): + if val is None: + return None + return converter(val) + + if not PY2: + sig = None + try: + sig = inspect.signature(converter) + except (ValueError, TypeError): # inspect failed + pass + if sig: + params = list(sig.parameters.values()) + if params and params[0].annotation is not inspect.Parameter.empty: + optional_converter.__annotations__["val"] = typing.Optional[ + params[0].annotation + ] + if sig.return_annotation is not inspect.Signature.empty: + optional_converter.__annotations__["return"] = typing.Optional[ + sig.return_annotation + ] + + return optional_converter + + +def default_if_none(default=NOTHING, factory=None): + """ + A converter that allows to replace ``None`` values by *default* or the + result of *factory*. + + :param default: Value to be used if ``None`` is passed. Passing an instance + of `attr.Factory` is supported, however the ``takes_self`` option + is *not*. + :param callable factory: A callable that takes no parameters whose result + is used if ``None`` is passed. + + :raises TypeError: If **neither** *default* or *factory* is passed. + :raises TypeError: If **both** *default* and *factory* are passed. + :raises ValueError: If an instance of `attr.Factory` is passed with + ``takes_self=True``. + + .. versionadded:: 18.2.0 + """ + if default is NOTHING and factory is None: + raise TypeError("Must pass either `default` or `factory`.") + + if default is not NOTHING and factory is not None: + raise TypeError( + "Must pass either `default` or `factory` but not both." + ) + + if factory is not None: + default = Factory(factory) + + if isinstance(default, Factory): + if default.takes_self: + raise ValueError( + "`takes_self` is not supported by default_if_none." + ) + + def default_if_none_converter(val): + if val is not None: + return val + + return default.factory() + + else: + + def default_if_none_converter(val): + if val is not None: + return val + + return default + + return default_if_none_converter diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/converters.pyi b/flask-app/venv/lib/python3.8/site-packages/attr/converters.pyi new file mode 100644 index 000000000..84a57590b --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attr/converters.pyi @@ -0,0 +1,13 @@ +from typing import Callable, Optional, TypeVar, overload + +from . import _ConverterType + + +_T = TypeVar("_T") + +def pipe(*validators: _ConverterType) -> _ConverterType: ... +def optional(converter: _ConverterType) -> _ConverterType: ... +@overload +def default_if_none(default: _T) -> _ConverterType: ... +@overload +def default_if_none(*, factory: Callable[[], _T]) -> _ConverterType: ... diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/exceptions.py b/flask-app/venv/lib/python3.8/site-packages/attr/exceptions.py new file mode 100644 index 000000000..f6f9861be --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attr/exceptions.py @@ -0,0 +1,92 @@ +from __future__ import absolute_import, division, print_function + + +class FrozenError(AttributeError): + """ + A frozen/immutable instance or attribute have been attempted to be + modified. + + It mirrors the behavior of ``namedtuples`` by using the same error message + and subclassing `AttributeError`. + + .. versionadded:: 20.1.0 + """ + + msg = "can't set attribute" + args = [msg] + + +class FrozenInstanceError(FrozenError): + """ + A frozen instance has been attempted to be modified. + + .. versionadded:: 16.1.0 + """ + + +class FrozenAttributeError(FrozenError): + """ + A frozen attribute has been attempted to be modified. + + .. versionadded:: 20.1.0 + """ + + +class AttrsAttributeNotFoundError(ValueError): + """ + An ``attrs`` function couldn't find an attribute that the user asked for. + + .. versionadded:: 16.2.0 + """ + + +class NotAnAttrsClassError(ValueError): + """ + A non-``attrs`` class has been passed into an ``attrs`` function. + + .. versionadded:: 16.2.0 + """ + + +class DefaultAlreadySetError(RuntimeError): + """ + A default has been set using ``attr.ib()`` and is attempted to be reset + using the decorator. + + .. versionadded:: 17.1.0 + """ + + +class UnannotatedAttributeError(RuntimeError): + """ + A class with ``auto_attribs=True`` has an ``attr.ib()`` without a type + annotation. + + .. versionadded:: 17.3.0 + """ + + +class PythonTooOldError(RuntimeError): + """ + It was attempted to use an ``attrs`` feature that requires a newer Python + version. + + .. versionadded:: 18.2.0 + """ + + +class NotCallableError(TypeError): + """ + A ``attr.ib()`` requiring a callable has been set with a value + that is not callable. + + .. versionadded:: 19.2.0 + """ + + def __init__(self, msg, value): + super(TypeError, self).__init__(msg, value) + self.msg = msg + self.value = value + + def __str__(self): + return str(self.msg) diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/exceptions.pyi b/flask-app/venv/lib/python3.8/site-packages/attr/exceptions.pyi new file mode 100644 index 000000000..a800fb26b --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attr/exceptions.pyi @@ -0,0 +1,18 @@ +from typing import Any + + +class FrozenError(AttributeError): + msg: str = ... + +class FrozenInstanceError(FrozenError): ... +class FrozenAttributeError(FrozenError): ... +class AttrsAttributeNotFoundError(ValueError): ... +class NotAnAttrsClassError(ValueError): ... +class DefaultAlreadySetError(RuntimeError): ... +class UnannotatedAttributeError(RuntimeError): ... +class PythonTooOldError(RuntimeError): ... + +class NotCallableError(TypeError): + msg: str = ... + value: Any = ... + def __init__(self, msg: str, value: Any) -> None: ... diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/filters.py b/flask-app/venv/lib/python3.8/site-packages/attr/filters.py new file mode 100644 index 000000000..dc47e8fa3 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attr/filters.py @@ -0,0 +1,52 @@ +""" +Commonly useful filters for `attr.asdict`. +""" + +from __future__ import absolute_import, division, print_function + +from ._compat import isclass +from ._make import Attribute + + +def _split_what(what): + """ + Returns a tuple of `frozenset`s of classes and attributes. + """ + return ( + frozenset(cls for cls in what if isclass(cls)), + frozenset(cls for cls in what if isinstance(cls, Attribute)), + ) + + +def include(*what): + """ + Whitelist *what*. + + :param what: What to whitelist. + :type what: `list` of `type` or `attr.Attribute`\\ s + + :rtype: `callable` + """ + cls, attrs = _split_what(what) + + def include_(attribute, value): + return value.__class__ in cls or attribute in attrs + + return include_ + + +def exclude(*what): + """ + Blacklist *what*. + + :param what: What to blacklist. + :type what: `list` of classes or `attr.Attribute`\\ s. + + :rtype: `callable` + """ + cls, attrs = _split_what(what) + + def exclude_(attribute, value): + return value.__class__ not in cls and attribute not in attrs + + return exclude_ diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/filters.pyi b/flask-app/venv/lib/python3.8/site-packages/attr/filters.pyi new file mode 100644 index 000000000..f7b63f1bb --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attr/filters.pyi @@ -0,0 +1,7 @@ +from typing import Any, Union + +from . import Attribute, _FilterType + + +def include(*what: Union[type, Attribute[Any]]) -> _FilterType[Any]: ... +def exclude(*what: Union[type, Attribute[Any]]) -> _FilterType[Any]: ... diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/py.typed b/flask-app/venv/lib/python3.8/site-packages/attr/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/setters.py b/flask-app/venv/lib/python3.8/site-packages/attr/setters.py new file mode 100644 index 000000000..240014b3c --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attr/setters.py @@ -0,0 +1,77 @@ +""" +Commonly used hooks for on_setattr. +""" + +from __future__ import absolute_import, division, print_function + +from . import _config +from .exceptions import FrozenAttributeError + + +def pipe(*setters): + """ + Run all *setters* and return the return value of the last one. + + .. versionadded:: 20.1.0 + """ + + def wrapped_pipe(instance, attrib, new_value): + rv = new_value + + for setter in setters: + rv = setter(instance, attrib, rv) + + return rv + + return wrapped_pipe + + +def frozen(_, __, ___): + """ + Prevent an attribute to be modified. + + .. versionadded:: 20.1.0 + """ + raise FrozenAttributeError() + + +def validate(instance, attrib, new_value): + """ + Run *attrib*'s validator on *new_value* if it has one. + + .. versionadded:: 20.1.0 + """ + if _config._run_validators is False: + return new_value + + v = attrib.validator + if not v: + return new_value + + v(instance, attrib, new_value) + + return new_value + + +def convert(instance, attrib, new_value): + """ + Run *attrib*'s converter -- if it has one -- on *new_value* and return the + result. + + .. versionadded:: 20.1.0 + """ + c = attrib.converter + if c: + return c(new_value) + + return new_value + + +NO_OP = object() +""" +Sentinel for disabling class-wide *on_setattr* hooks for certain attributes. + +Does not work in `pipe` or within lists. + +.. versionadded:: 20.1.0 +""" diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/setters.pyi b/flask-app/venv/lib/python3.8/site-packages/attr/setters.pyi new file mode 100644 index 000000000..a921e07de --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attr/setters.pyi @@ -0,0 +1,20 @@ +from typing import Any, NewType, NoReturn, TypeVar, cast + +from . import Attribute, _OnSetAttrType + + +_T = TypeVar("_T") + +def frozen( + instance: Any, attribute: Attribute[Any], new_value: Any +) -> NoReturn: ... +def pipe(*setters: _OnSetAttrType) -> _OnSetAttrType: ... +def validate(instance: Any, attribute: Attribute[_T], new_value: _T) -> _T: ... + +# convert is allowed to return Any, because they can be chained using pipe. +def convert( + instance: Any, attribute: Attribute[Any], new_value: Any +) -> Any: ... + +_NoOpType = NewType("_NoOpType", object) +NO_OP: _NoOpType diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/validators.py b/flask-app/venv/lib/python3.8/site-packages/attr/validators.py new file mode 100644 index 000000000..b9a73054e --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attr/validators.py @@ -0,0 +1,379 @@ +""" +Commonly useful validators. +""" + +from __future__ import absolute_import, division, print_function + +import re + +from ._make import _AndValidator, and_, attrib, attrs +from .exceptions import NotCallableError + + +__all__ = [ + "and_", + "deep_iterable", + "deep_mapping", + "in_", + "instance_of", + "is_callable", + "matches_re", + "optional", + "provides", +] + + +@attrs(repr=False, slots=True, hash=True) +class _InstanceOfValidator(object): + type = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not isinstance(value, self.type): + raise TypeError( + "'{name}' must be {type!r} (got {value!r} that is a " + "{actual!r}).".format( + name=attr.name, + type=self.type, + actual=value.__class__, + value=value, + ), + attr, + self.type, + value, + ) + + def __repr__(self): + return "".format( + type=self.type + ) + + +def instance_of(type): + """ + A validator that raises a `TypeError` if the initializer is called + with a wrong type for this particular attribute (checks are performed using + `isinstance` therefore it's also valid to pass a tuple of types). + + :param type: The type to check for. + :type type: type or tuple of types + + :raises TypeError: With a human readable error message, the attribute + (of type `attr.Attribute`), the expected type, and the value it + got. + """ + return _InstanceOfValidator(type) + + +@attrs(repr=False, frozen=True, slots=True) +class _MatchesReValidator(object): + regex = attrib() + flags = attrib() + match_func = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not self.match_func(value): + raise ValueError( + "'{name}' must match regex {regex!r}" + " ({value!r} doesn't)".format( + name=attr.name, regex=self.regex.pattern, value=value + ), + attr, + self.regex, + value, + ) + + def __repr__(self): + return "".format( + regex=self.regex + ) + + +def matches_re(regex, flags=0, func=None): + r""" + A validator that raises `ValueError` if the initializer is called + with a string that doesn't match *regex*. + + :param str regex: a regex string to match against + :param int flags: flags that will be passed to the underlying re function + (default 0) + :param callable func: which underlying `re` function to call (options + are `re.fullmatch`, `re.search`, `re.match`, default + is ``None`` which means either `re.fullmatch` or an emulation of + it on Python 2). For performance reasons, they won't be used directly + but on a pre-`re.compile`\ ed pattern. + + .. versionadded:: 19.2.0 + """ + fullmatch = getattr(re, "fullmatch", None) + valid_funcs = (fullmatch, None, re.search, re.match) + if func not in valid_funcs: + raise ValueError( + "'func' must be one of %s." + % ( + ", ".join( + sorted( + e and e.__name__ or "None" for e in set(valid_funcs) + ) + ), + ) + ) + + pattern = re.compile(regex, flags) + if func is re.match: + match_func = pattern.match + elif func is re.search: + match_func = pattern.search + else: + if fullmatch: + match_func = pattern.fullmatch + else: + pattern = re.compile(r"(?:{})\Z".format(regex), flags) + match_func = pattern.match + + return _MatchesReValidator(pattern, flags, match_func) + + +@attrs(repr=False, slots=True, hash=True) +class _ProvidesValidator(object): + interface = attrib() + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not self.interface.providedBy(value): + raise TypeError( + "'{name}' must provide {interface!r} which {value!r} " + "doesn't.".format( + name=attr.name, interface=self.interface, value=value + ), + attr, + self.interface, + value, + ) + + def __repr__(self): + return "".format( + interface=self.interface + ) + + +def provides(interface): + """ + A validator that raises a `TypeError` if the initializer is called + with an object that does not provide the requested *interface* (checks are + performed using ``interface.providedBy(value)`` (see `zope.interface + `_). + + :param interface: The interface to check for. + :type interface: ``zope.interface.Interface`` + + :raises TypeError: With a human readable error message, the attribute + (of type `attr.Attribute`), the expected interface, and the + value it got. + """ + return _ProvidesValidator(interface) + + +@attrs(repr=False, slots=True, hash=True) +class _OptionalValidator(object): + validator = attrib() + + def __call__(self, inst, attr, value): + if value is None: + return + + self.validator(inst, attr, value) + + def __repr__(self): + return "".format( + what=repr(self.validator) + ) + + +def optional(validator): + """ + A validator that makes an attribute optional. An optional attribute is one + which can be set to ``None`` in addition to satisfying the requirements of + the sub-validator. + + :param validator: A validator (or a list of validators) that is used for + non-``None`` values. + :type validator: callable or `list` of callables. + + .. versionadded:: 15.1.0 + .. versionchanged:: 17.1.0 *validator* can be a list of validators. + """ + if isinstance(validator, list): + return _OptionalValidator(_AndValidator(validator)) + return _OptionalValidator(validator) + + +@attrs(repr=False, slots=True, hash=True) +class _InValidator(object): + options = attrib() + + def __call__(self, inst, attr, value): + try: + in_options = value in self.options + except TypeError: # e.g. `1 in "abc"` + in_options = False + + if not in_options: + raise ValueError( + "'{name}' must be in {options!r} (got {value!r})".format( + name=attr.name, options=self.options, value=value + ) + ) + + def __repr__(self): + return "".format( + options=self.options + ) + + +def in_(options): + """ + A validator that raises a `ValueError` if the initializer is called + with a value that does not belong in the options provided. The check is + performed using ``value in options``. + + :param options: Allowed options. + :type options: list, tuple, `enum.Enum`, ... + + :raises ValueError: With a human readable error message, the attribute (of + type `attr.Attribute`), the expected options, and the value it + got. + + .. versionadded:: 17.1.0 + """ + return _InValidator(options) + + +@attrs(repr=False, slots=False, hash=True) +class _IsCallableValidator(object): + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if not callable(value): + message = ( + "'{name}' must be callable " + "(got {value!r} that is a {actual!r})." + ) + raise NotCallableError( + msg=message.format( + name=attr.name, value=value, actual=value.__class__ + ), + value=value, + ) + + def __repr__(self): + return "" + + +def is_callable(): + """ + A validator that raises a `attr.exceptions.NotCallableError` if the + initializer is called with a value for this particular attribute + that is not callable. + + .. versionadded:: 19.1.0 + + :raises `attr.exceptions.NotCallableError`: With a human readable error + message containing the attribute (`attr.Attribute`) name, + and the value it got. + """ + return _IsCallableValidator() + + +@attrs(repr=False, slots=True, hash=True) +class _DeepIterable(object): + member_validator = attrib(validator=is_callable()) + iterable_validator = attrib( + default=None, validator=optional(is_callable()) + ) + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if self.iterable_validator is not None: + self.iterable_validator(inst, attr, value) + + for member in value: + self.member_validator(inst, attr, member) + + def __repr__(self): + iterable_identifier = ( + "" + if self.iterable_validator is None + else " {iterable!r}".format(iterable=self.iterable_validator) + ) + return ( + "" + ).format( + iterable_identifier=iterable_identifier, + member=self.member_validator, + ) + + +def deep_iterable(member_validator, iterable_validator=None): + """ + A validator that performs deep validation of an iterable. + + :param member_validator: Validator to apply to iterable members + :param iterable_validator: Validator to apply to iterable itself + (optional) + + .. versionadded:: 19.1.0 + + :raises TypeError: if any sub-validators fail + """ + return _DeepIterable(member_validator, iterable_validator) + + +@attrs(repr=False, slots=True, hash=True) +class _DeepMapping(object): + key_validator = attrib(validator=is_callable()) + value_validator = attrib(validator=is_callable()) + mapping_validator = attrib(default=None, validator=optional(is_callable())) + + def __call__(self, inst, attr, value): + """ + We use a callable class to be able to change the ``__repr__``. + """ + if self.mapping_validator is not None: + self.mapping_validator(inst, attr, value) + + for key in value: + self.key_validator(inst, attr, key) + self.value_validator(inst, attr, value[key]) + + def __repr__(self): + return ( + "" + ).format(key=self.key_validator, value=self.value_validator) + + +def deep_mapping(key_validator, value_validator, mapping_validator=None): + """ + A validator that performs deep validation of a dictionary. + + :param key_validator: Validator to apply to dictionary keys + :param value_validator: Validator to apply to dictionary values + :param mapping_validator: Validator to apply to top-level mapping + attribute (optional) + + .. versionadded:: 19.1.0 + + :raises TypeError: if any sub-validators fail + """ + return _DeepMapping(key_validator, value_validator, mapping_validator) diff --git a/flask-app/venv/lib/python3.8/site-packages/attr/validators.pyi b/flask-app/venv/lib/python3.8/site-packages/attr/validators.pyi new file mode 100644 index 000000000..fe92aac42 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attr/validators.pyi @@ -0,0 +1,68 @@ +from typing import ( + Any, + AnyStr, + Callable, + Container, + Iterable, + List, + Mapping, + Match, + Optional, + Tuple, + Type, + TypeVar, + Union, + overload, +) + +from . import _ValidatorType + + +_T = TypeVar("_T") +_T1 = TypeVar("_T1") +_T2 = TypeVar("_T2") +_T3 = TypeVar("_T3") +_I = TypeVar("_I", bound=Iterable) +_K = TypeVar("_K") +_V = TypeVar("_V") +_M = TypeVar("_M", bound=Mapping) + +# To be more precise on instance_of use some overloads. +# If there are more than 3 items in the tuple then we fall back to Any +@overload +def instance_of(type: Type[_T]) -> _ValidatorType[_T]: ... +@overload +def instance_of(type: Tuple[Type[_T]]) -> _ValidatorType[_T]: ... +@overload +def instance_of( + type: Tuple[Type[_T1], Type[_T2]] +) -> _ValidatorType[Union[_T1, _T2]]: ... +@overload +def instance_of( + type: Tuple[Type[_T1], Type[_T2], Type[_T3]] +) -> _ValidatorType[Union[_T1, _T2, _T3]]: ... +@overload +def instance_of(type: Tuple[type, ...]) -> _ValidatorType[Any]: ... +def provides(interface: Any) -> _ValidatorType[Any]: ... +def optional( + validator: Union[_ValidatorType[_T], List[_ValidatorType[_T]]] +) -> _ValidatorType[Optional[_T]]: ... +def in_(options: Container[_T]) -> _ValidatorType[_T]: ... +def and_(*validators: _ValidatorType[_T]) -> _ValidatorType[_T]: ... +def matches_re( + regex: AnyStr, + flags: int = ..., + func: Optional[ + Callable[[AnyStr, AnyStr, int], Optional[Match[AnyStr]]] + ] = ..., +) -> _ValidatorType[AnyStr]: ... +def deep_iterable( + member_validator: _ValidatorType[_T], + iterable_validator: Optional[_ValidatorType[_I]] = ..., +) -> _ValidatorType[_I]: ... +def deep_mapping( + key_validator: _ValidatorType[_K], + value_validator: _ValidatorType[_V], + mapping_validator: Optional[_ValidatorType[_M]] = ..., +) -> _ValidatorType[_M]: ... +def is_callable() -> _ValidatorType[_T]: ... diff --git a/flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/AUTHORS.rst b/flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/AUTHORS.rst new file mode 100644 index 000000000..f14ef6c60 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/AUTHORS.rst @@ -0,0 +1,11 @@ +Credits +======= + +``attrs`` is written and maintained by `Hynek Schlawack `_. + +The development is kindly supported by `Variomedia AG `_. + +A full list of contributors can be found in `GitHub's overview `_. + +It’s the spiritual successor of `characteristic `_ and aspires to fix some of it clunkiness and unfortunate decisions. +Both were inspired by Twisted’s `FancyEqMixin `_ but both are implemented using class decorators because `subclassing is bad for you `_, m’kay? diff --git a/flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/INSTALLER b/flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/LICENSE b/flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/LICENSE new file mode 100644 index 000000000..7ae3df930 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Hynek Schlawack + +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. diff --git a/flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/METADATA b/flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/METADATA new file mode 100644 index 000000000..ceca5b9ad --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/METADATA @@ -0,0 +1,211 @@ +Metadata-Version: 2.1 +Name: attrs +Version: 21.2.0 +Summary: Classes Without Boilerplate +Home-page: https://www.attrs.org/ +Author: Hynek Schlawack +Author-email: hs@ox.cx +Maintainer: Hynek Schlawack +Maintainer-email: hs@ox.cx +License: MIT +Project-URL: Documentation, https://www.attrs.org/ +Project-URL: Changelog, https://www.attrs.org/en/stable/changelog.html +Project-URL: Bug Tracker, https://github.com/python-attrs/attrs/issues +Project-URL: Source Code, https://github.com/python-attrs/attrs +Project-URL: Funding, https://github.com/sponsors/hynek +Project-URL: Tidelift, https://tidelift.com/subscription/pkg/pypi-attrs?utm_source=pypi-attrs&utm_medium=pypi +Project-URL: Ko-fi, https://ko-fi.com/the_hynek +Keywords: class,attribute,boilerplate +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Natural Language :: English +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.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 :: 3.10 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Topic :: Software Development :: Libraries :: Python Modules +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* +Description-Content-Type: text/x-rst +Provides-Extra: dev +Requires-Dist: coverage[toml] (>=5.0.2) ; extra == 'dev' +Requires-Dist: hypothesis ; extra == 'dev' +Requires-Dist: pympler ; extra == 'dev' +Requires-Dist: pytest (>=4.3.0) ; extra == 'dev' +Requires-Dist: six ; extra == 'dev' +Requires-Dist: mypy ; extra == 'dev' +Requires-Dist: pytest-mypy-plugins ; extra == 'dev' +Requires-Dist: zope.interface ; extra == 'dev' +Requires-Dist: furo ; extra == 'dev' +Requires-Dist: sphinx ; extra == 'dev' +Requires-Dist: sphinx-notfound-page ; extra == 'dev' +Requires-Dist: pre-commit ; extra == 'dev' +Provides-Extra: docs +Requires-Dist: furo ; extra == 'docs' +Requires-Dist: sphinx ; extra == 'docs' +Requires-Dist: zope.interface ; extra == 'docs' +Requires-Dist: sphinx-notfound-page ; extra == 'docs' +Provides-Extra: tests +Requires-Dist: coverage[toml] (>=5.0.2) ; extra == 'tests' +Requires-Dist: hypothesis ; extra == 'tests' +Requires-Dist: pympler ; extra == 'tests' +Requires-Dist: pytest (>=4.3.0) ; extra == 'tests' +Requires-Dist: six ; extra == 'tests' +Requires-Dist: mypy ; extra == 'tests' +Requires-Dist: pytest-mypy-plugins ; extra == 'tests' +Requires-Dist: zope.interface ; extra == 'tests' +Provides-Extra: tests_no_zope +Requires-Dist: coverage[toml] (>=5.0.2) ; extra == 'tests_no_zope' +Requires-Dist: hypothesis ; extra == 'tests_no_zope' +Requires-Dist: pympler ; extra == 'tests_no_zope' +Requires-Dist: pytest (>=4.3.0) ; extra == 'tests_no_zope' +Requires-Dist: six ; extra == 'tests_no_zope' +Requires-Dist: mypy ; extra == 'tests_no_zope' +Requires-Dist: pytest-mypy-plugins ; extra == 'tests_no_zope' + +====================================== +``attrs``: Classes Without Boilerplate +====================================== + + +``attrs`` is the Python package that will bring back the **joy** of **writing classes** by relieving you from the drudgery of implementing object protocols (aka `dunder `_ methods). +`Trusted by NASA `_ for Mars missions since 2020! + +Its main goal is to help you to write **concise** and **correct** software without slowing down your code. + +.. teaser-end + +For that, it gives you a class decorator and a way to declaratively define the attributes on that class: + +.. -code-begin- + +.. code-block:: pycon + + >>> import attr + + >>> @attr.s + ... class SomeClass(object): + ... a_number = attr.ib(default=42) + ... list_of_numbers = attr.ib(factory=list) + ... + ... def hard_math(self, another_number): + ... return self.a_number + sum(self.list_of_numbers) * another_number + + + >>> sc = SomeClass(1, [1, 2, 3]) + >>> sc + SomeClass(a_number=1, list_of_numbers=[1, 2, 3]) + + >>> sc.hard_math(3) + 19 + >>> sc == SomeClass(1, [1, 2, 3]) + True + >>> sc != SomeClass(2, [3, 2, 1]) + True + + >>> attr.asdict(sc) + {'a_number': 1, 'list_of_numbers': [1, 2, 3]} + + >>> SomeClass() + SomeClass(a_number=42, list_of_numbers=[]) + + >>> C = attr.make_class("C", ["a", "b"]) + >>> C("foo", "bar") + C(a='foo', b='bar') + + +After *declaring* your attributes ``attrs`` gives you: + +- a concise and explicit overview of the class's attributes, +- a nice human-readable ``__repr__``, +- a complete set of comparison methods (equality and ordering), +- an initializer, +- and much more, + +*without* writing dull boilerplate code again and again and *without* runtime performance penalties. + +On Python 3.6 and later, you can often even drop the calls to ``attr.ib()`` by using `type annotations `_. + +This gives you the power to use actual classes with actual types in your code instead of confusing ``tuple``\ s or `confusingly behaving `_ ``namedtuple``\ s. +Which in turn encourages you to write *small classes* that do `one thing well `_. +Never again violate the `single responsibility principle `_ just because implementing ``__init__`` et al is a painful drag. + + +.. -getting-help- + +Getting Help +============ + +Please use the ``python-attrs`` tag on `StackOverflow `_ to get help. + +Answering questions of your fellow developers is also a great way to help the project! + + +.. -project-information- + +Project Information +=================== + +``attrs`` is released under the `MIT `_ license, +its documentation lives at `Read the Docs `_, +the code on `GitHub `_, +and the latest release on `PyPI `_. +It’s rigorously tested on Python 2.7, 3.5+, and PyPy. + +We collect information on **third-party extensions** in our `wiki `_. +Feel free to browse and add your own! + +If you'd like to contribute to ``attrs`` you're most welcome and we've written `a little guide `_ to get you started! + + +``attrs`` for Enterprise +------------------------ + +Available as part of the Tidelift Subscription. + +The maintainers of ``attrs`` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source packages you use to build your applications. +Save time, reduce risk, and improve code health, while paying the maintainers of the exact packages you use. +`Learn more. `_ + + +Release Information +=================== + +21.2.0 (2021-05-07) +------------------- + +Backward-incompatible Changes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +- We had to revert the recursive feature for ``attr.evolve()`` because it broke some use-cases -- sorry! + `#806 `_ +- Python 3.4 is now blocked using packaging metadata because ``attrs`` can't be imported on it anymore. + To ensure that 3.4 users can keep installing ``attrs`` easily, we will `yank `_ 21.1.0 from PyPI. + This has **no** consequences if you pin ``attrs`` to 21.1.0. + `#807 `_ + +`Full changelog `_. + +Credits +======= + +``attrs`` is written and maintained by `Hynek Schlawack `_. + +The development is kindly supported by `Variomedia AG `_. + +A full list of contributors can be found in `GitHub's overview `_. + +It’s the spiritual successor of `characteristic `_ and aspires to fix some of it clunkiness and unfortunate decisions. +Both were inspired by Twisted’s `FancyEqMixin `_ but both are implemented using class decorators because `subclassing is bad for you `_, m’kay? + + diff --git a/flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/RECORD b/flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/RECORD new file mode 100644 index 000000000..1f361a7b8 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/RECORD @@ -0,0 +1,42 @@ +attr/__init__.py,sha256=OlF8DYZfrT1KFU6VKHkW4ia76ntgvvaqBbsNf8j6Woc,1613 +attr/__init__.pyi,sha256=bNx5qLa3MBtgaf9P5OP2uwuv0xbS7HUrEfLlkrAsrr8,14837 +attr/__pycache__/__init__.cpython-38.pyc,, +attr/__pycache__/_cmp.cpython-38.pyc,, +attr/__pycache__/_compat.cpython-38.pyc,, +attr/__pycache__/_config.cpython-38.pyc,, +attr/__pycache__/_funcs.cpython-38.pyc,, +attr/__pycache__/_make.cpython-38.pyc,, +attr/__pycache__/_next_gen.cpython-38.pyc,, +attr/__pycache__/_version_info.cpython-38.pyc,, +attr/__pycache__/converters.cpython-38.pyc,, +attr/__pycache__/exceptions.cpython-38.pyc,, +attr/__pycache__/filters.cpython-38.pyc,, +attr/__pycache__/setters.cpython-38.pyc,, +attr/__pycache__/validators.cpython-38.pyc,, +attr/_cmp.py,sha256=CB01fdAcVk9Uwho7qdhrpK1ss9lilIeKoY-WJ-EaZYA,4133 +attr/_cmp.pyi,sha256=APRWqmFwHtTrapyy-vNKovjF9dA-HPi-AqqidjgvLpQ,318 +attr/_compat.py,sha256=hYZsXQOKJzAIAPPEzo-Y4aF0DMjhCXEp-nr1gSxVVG4,7562 +attr/_config.py,sha256=_KvW0mQdH2PYjHc0YfIUaV_o2pVfM7ziMEYTxwmEhOA,514 +attr/_funcs.py,sha256=azJeF9YIMg3lP2qeQyuGhrrcJkfTjm7OLm2u4MhPTqs,13398 +attr/_make.py,sha256=xrK0rSAYDINJF-yGgb_Qb2DHuEaKRmrs102mkO0LI5c,97743 +attr/_next_gen.py,sha256=aZEIlr2XlPVzJ_SWSNRAEx07jgqbtHWQm3PnaOXTMyw,4072 +attr/_version_info.py,sha256=azMi1lNelb3cJvvYUMXsXVbUANkRzbD5IEiaXVpeVr4,2162 +attr/_version_info.pyi,sha256=x_M3L3WuB7r_ULXAWjx959udKQ4HLB8l-hsc1FDGNvk,209 +attr/converters.py,sha256=mn8pLVYzzl-WvmlNe52HM2ukSkuO4a12mrTaHpQjX9c,3039 +attr/converters.pyi,sha256=L7eN2rEXCNVOkh1hYP-GVbWtyO3e6eKOBvJR-hK_h1M,382 +attr/exceptions.py,sha256=6dC-9b6_nTG066z9sw0TP_Tx4vJaIC5RImMONTkDM6Q,1949 +attr/exceptions.pyi,sha256=Ydjpt9xbNLM8HUEhayegA3c0xIBc75kpRgtiv0qsLCs,540 +attr/filters.py,sha256=weDxwATsa69T_0bPVjiM1fGsciAMQmwhY5G8Jm5BxuI,1098 +attr/filters.pyi,sha256=jUFN1Nqx2x5ayyLLHzsW5hHObjd6RudZjnj-ENAJdWk,216 +attr/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +attr/setters.py,sha256=0ElzHwdVK3dsYcQi2CXkFvhx8fNxUI5OVhw8SWeaKmA,1434 +attr/setters.pyi,sha256=kTxNSnrItMgRpFDyIwvtFd6xYtqnOWwi4UVks4dskRY,574 +attr/validators.py,sha256=6DBx1jt4oZxx1ppvx6JWqm9-UAsYpXC4HTwxJilCeRg,11497 +attr/validators.pyi,sha256=qN6dsUdWh2UkLaX46JJ86lzYlhy4sh8z66fTXgJQO60,1870 +attrs-21.2.0.dist-info/AUTHORS.rst,sha256=wsqCNbGz_mklcJrt54APIZHZpoTIJLkXqEhhn4Nd8hc,752 +attrs-21.2.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +attrs-21.2.0.dist-info/LICENSE,sha256=v2WaKLSSQGAvVrvfSQy-LsUJsVuY-Z17GaUsdA4yeGM,1082 +attrs-21.2.0.dist-info/METADATA,sha256=oaarWZ5r9x96ZwIcBvpmzpyt6ADyZP2QYjYVZrJrrEQ,9097 +attrs-21.2.0.dist-info/RECORD,, +attrs-21.2.0.dist-info/WHEEL,sha256=Z-nyYpwrcSqxfdux5Mbn_DQ525iP7J2DG3JgGvOYyTQ,110 +attrs-21.2.0.dist-info/top_level.txt,sha256=tlRYMddkRlKPqJ96wP2_j9uEsmcNHgD2SbuWd4CzGVU,5 diff --git a/flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/WHEEL b/flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/WHEEL new file mode 100644 index 000000000..01b8fc7d4 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/WHEEL @@ -0,0 +1,6 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.36.2) +Root-Is-Purelib: true +Tag: py2-none-any +Tag: py3-none-any + diff --git a/flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/top_level.txt b/flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/top_level.txt new file mode 100644 index 000000000..66a062d88 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/attrs-21.2.0.dist-info/top_level.txt @@ -0,0 +1 @@ +attr diff --git a/flask-app/venv/lib/python3.8/site-packages/benchmarks/__init__.py b/flask-app/venv/lib/python3.8/site-packages/benchmarks/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/flask-app/venv/lib/python3.8/site-packages/benchmarks/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/benchmarks/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c5aa9d1672c310dc67feb1967b64777347523d82 GIT binary patch literal 199 zcmWIL<>g`k0@HaD6G8N25P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;x_cenC-YSz<}5 zepzCXep-G`N@`KDewCrVWl~;wmVsH8QIVl>UV%Y;MS7m4WtE8m2&5P2hv*k%7U*W? z6_+IDWmSIU+ZS73n{ zS6%c;^XARG(UazBu{aG7*PkDJgZmWJ$A$XdQDPEUSbE{g^Vb84%ac_O#LlWgD%?^P{f9&bE1!Gmu zI| o3<9xIv7`w75Z(P-fS=I>QbAOy;nNR#;eq$GVlmSnWfUM!^~wrnrs*v2|{$1 qml6u8%jbsM7Y9WHj47=;#?H+!#kWl>UTb;hA8|3#oXjIi=-)qY!c1=f literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/benchmarks/bm/__pycache__/_scenario.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/benchmarks/bm/__pycache__/_scenario.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7b13badefeec654ea10b7d681d1c1815db453646 GIT binary patch literal 2643 zcmZuy&2JM&6rY*>@OrZjlme|Nw63TqMvWapK}{7!d{nA>XjBoZ(v{F^vom(q*t=$C z0>s#-M1mfuRP|oj>WP18u08c%;DGwxI8Fj(Rx`6RZ)V>2@BLh>g#_C5zy3%&0U`h3 z;&}66aTB`Qf`JoGdnBICG^TdVVrFM2cI@oNu3%j+_BrFuK8Y*e6Yg?vpKwo*Zm{RX zRoL-)1$HXJorIz~rLeoe=_Y9e{{kPfkx`ruS_Nhgx&vM9!|bg47m4_QS#{Wh)t?f@*8szA8 zw)?4km=~R9#z5qGLmj}*F!?9LS~Yr`k9Q z&Q>}?zpuLrGx%pa#(v>vCj)h>G+)Nc*1wpO=_ zC*9>6-4%KDS}|Boo_31W)#2C6*gAvOz1AQfEaipLX|K2RcsN+y-R+AO=US$jXdN0% zwYK`LWX`lX*fl3p&J&QVmlq;I;ZC6GFfIvQq!ArnOyq$dnrpo>OM7a)`R<(tl^?(b ziLQ7>jjMMD;;k4%0={Yeav6SdG9nM^7+){Qh;}J>#Kl>&`T9@x$lW9o#c4Pa161M* z5)-Py3~MgS8vatVFi52m+)8iH%acGb;Tq2~ot6E;`gvF@j0>MJ8ohDJ@WOkvIy%46 zs2P6%O2~HX-`yz+A>%3-$1;)02kOrF7W_PRAd z^-#wM2 zM-bc5=0Se`2;rWgY32?857FfYEHu${ME2Ok=HkeiI+P$XOd%ow7hvpq+JETC705`~ z0X%>#d_^}P|2(w=K9QfoGbTuqyvTKuSa%qzOqeA7lJE3Hk{}iB3RKjs;X?NHl=Y4A zwo)Y^2dxnyTB`{pU#b+G#bC3+BR_|~FQMD0A@sBpxgi~%Ij&}NE_d-|LW@cxWAlxkEUO9pQF80R`bD zjVmJH4tMv#aB)?H-39J(->xEF;Q**2RHsvW`{Z?jUB|#tGfo?+#Fcp1ByKLg1PrL4vSri?5&&CL&-YicVaKYNmc)juo zconl7!9$5&d*+EFcbo&-H^>3)L)h@_T==Fs2t4cV|4D|@S;;1GyJ3`j=cB<>U7Ttl0d8+fWsOu6;P={o+ra@FdgfB8t*FtG5LK`!Io51BA z(TxN5)?j}ox-&dXl_n70Dqd)SDs(F_haV_WWJ;iHLD7QLaI`fk7TpoW#F(u|?#E>8 zaWcF#jAkb(`GpjxU83EGq5O#w{C*_`yAS)-$cPIW{vdxPm^!=Ub1=N)7#dKQMNlq6$EBewk=BUhw=l^S z92^`zf&mJB{#0Ug8V9Pj1sqekb^8ut;S8%5pX7Qw0U1)8VfL~2NnnDt8G@~2;3`CM PFch=Ka(pK^cdGUuu&ZkL literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/benchmarks/bm/_scenario.py b/flask-app/venv/lib/python3.8/site-packages/benchmarks/bm/_scenario.py new file mode 100644 index 000000000..4b95aa24a --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/benchmarks/bm/_scenario.py @@ -0,0 +1,77 @@ +import abc +import time + +import attr +import pyperf +import six + + +var = attr.ib + + +def _register(scenario_cls): + """Registers a scenario for benchmarking.""" + # This extends pyperf's runner by registering arguments for the scenario config + def add_cmdline_args(cmd, args): + for field in attr.fields(scenario_cls): + if hasattr(args, field.name): + cmd.extend(("--{}".format(field.name), str(getattr(args, field.name)))) + + runner = pyperf.Runner(add_cmdline_args=add_cmdline_args) + cmd = runner.argparser + + for field in attr.fields(scenario_cls): + cmd.add_argument("--{}".format(field.name), type=field.type) + + parsed_args = runner.parse_args() + + config_dict = { + field.name: getattr(parsed_args, field.name) + for field in attr.fields(scenario_cls) + if hasattr(parsed_args, field.name) + } + scenario = scenario_cls(**config_dict) + + runner.bench_time_func(scenario.scenario_name, scenario._pyperf) + + +class ScenarioMeta(abc.ABCMeta): + def __init__(cls, name, bases, _dict): + super(ScenarioMeta, cls).__init__(name, bases, _dict) + + # Make sure every sub-class is wrapped by `attr.s` + cls = attr.s()(cls) + + # Do not register the base Scenario class + # DEV: We cannot compare `cls` to `Scenario` since it doesn't exist yet + if cls.__module__ != __name__: + _register(cls) + + +class Scenario(six.with_metaclass(ScenarioMeta)): + """The base class for specifying a benchmark.""" + + name = attr.ib(type=str) + + @property + def scenario_name(self): + return "{}-{}".format(self.__class__.__name__.lower(), self.name) + + @abc.abstractmethod + def run(self): + """Returns a context manager that yields a function to be run for performance testing.""" + pass + + def _pyperf(self, loops): + rungen = self.run() + run = next(rungen) + t0 = time.perf_counter() + run(loops) + dt = time.perf_counter() - t0 + try: + # perform any teardown + next(rungen) + except StopIteration: + pass + finally: + return dt diff --git a/flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/INSTALLER b/flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/LICENSE.rst b/flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/LICENSE.rst new file mode 100644 index 000000000..d12a84918 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/LICENSE.rst @@ -0,0 +1,28 @@ +Copyright 2014 Pallets + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/METADATA b/flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/METADATA new file mode 100644 index 000000000..2c8da01a5 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/METADATA @@ -0,0 +1,111 @@ +Metadata-Version: 2.1 +Name: click +Version: 8.0.3 +Summary: Composable command line interface toolkit +Home-page: https://palletsprojects.com/p/click/ +Author: Armin Ronacher +Author-email: armin.ronacher@active-4.com +Maintainer: Pallets +Maintainer-email: contact@palletsprojects.com +License: BSD-3-Clause +Project-URL: Donate, https://palletsprojects.com/donate +Project-URL: Documentation, https://click.palletsprojects.com/ +Project-URL: Changes, https://click.palletsprojects.com/changes/ +Project-URL: Source Code, https://github.com/pallets/click/ +Project-URL: Issue Tracker, https://github.com/pallets/click/issues/ +Project-URL: Twitter, https://twitter.com/PalletsTeam +Project-URL: Chat, https://discord.gg/pallets +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: BSD License +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python +Requires-Python: >=3.6 +Description-Content-Type: text/x-rst +License-File: LICENSE.rst +Requires-Dist: colorama ; platform_system == "Windows" +Requires-Dist: importlib-metadata ; python_version < "3.8" + +\$ click\_ +========== + +Click is a Python package for creating beautiful command line interfaces +in a composable way with as little code as necessary. It's the "Command +Line Interface Creation Kit". It's highly configurable but comes with +sensible defaults out of the box. + +It aims to make the process of writing command line tools quick and fun +while also preventing any frustration caused by the inability to +implement an intended CLI API. + +Click in three points: + +- Arbitrary nesting of commands +- Automatic help page generation +- Supports lazy loading of subcommands at runtime + + +Installing +---------- + +Install and update using `pip`_: + +.. code-block:: text + + $ pip install -U click + +.. _pip: https://pip.pypa.io/en/stable/getting-started/ + + +A Simple Example +---------------- + +.. code-block:: python + + import click + + @click.command() + @click.option("--count", default=1, help="Number of greetings.") + @click.option("--name", prompt="Your name", help="The person to greet.") + def hello(count, name): + """Simple program that greets NAME for a total of COUNT times.""" + for _ in range(count): + click.echo(f"Hello, {name}!") + + if __name__ == '__main__': + hello() + +.. code-block:: text + + $ python hello.py --count=3 + Your name: Click + Hello, Click! + Hello, Click! + Hello, Click! + + +Donate +------ + +The Pallets organization develops and supports Click and other popular +packages. In order to grow the community of contributors and users, and +allow the maintainers to devote more time to the projects, `please +donate today`_. + +.. _please donate today: https://palletsprojects.com/donate + + +Links +----- + +- Documentation: https://click.palletsprojects.com/ +- Changes: https://click.palletsprojects.com/changes/ +- PyPI Releases: https://pypi.org/project/click/ +- Source Code: https://github.com/pallets/click +- Issue Tracker: https://github.com/pallets/click/issues +- Website: https://palletsprojects.com/p/click +- Twitter: https://twitter.com/PalletsTeam +- Chat: https://discord.gg/pallets + + diff --git a/flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/RECORD b/flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/RECORD new file mode 100644 index 000000000..263a6c699 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/RECORD @@ -0,0 +1,41 @@ +click-8.0.3.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +click-8.0.3.dist-info/LICENSE.rst,sha256=morRBqOU6FO_4h9C9OctWSgZoigF2ZG18ydQKSkrZY0,1475 +click-8.0.3.dist-info/METADATA,sha256=_0jCOf3DdGPvKUZUlBukeb1t6Pnxmm_OMGpaBoDthfc,3247 +click-8.0.3.dist-info/RECORD,, +click-8.0.3.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92 +click-8.0.3.dist-info/top_level.txt,sha256=J1ZQogalYS4pphY_lPECoNMfw0HzTSrZglC4Yfwo4xA,6 +click/__init__.py,sha256=YkIrDg7-0g5aBS6D2pDe58j3MOaFylHED2_8OXh2fnM,3243 +click/__pycache__/__init__.cpython-38.pyc,, +click/__pycache__/_compat.cpython-38.pyc,, +click/__pycache__/_termui_impl.cpython-38.pyc,, +click/__pycache__/_textwrap.cpython-38.pyc,, +click/__pycache__/_unicodefun.cpython-38.pyc,, +click/__pycache__/_winconsole.cpython-38.pyc,, +click/__pycache__/core.cpython-38.pyc,, +click/__pycache__/decorators.cpython-38.pyc,, +click/__pycache__/exceptions.cpython-38.pyc,, +click/__pycache__/formatting.cpython-38.pyc,, +click/__pycache__/globals.cpython-38.pyc,, +click/__pycache__/parser.cpython-38.pyc,, +click/__pycache__/shell_completion.cpython-38.pyc,, +click/__pycache__/termui.cpython-38.pyc,, +click/__pycache__/testing.cpython-38.pyc,, +click/__pycache__/types.cpython-38.pyc,, +click/__pycache__/utils.cpython-38.pyc,, +click/_compat.py,sha256=P15KQumAZC2F2MFe_JSRbvVOJcNosQfMDrdZq0ReCLM,18814 +click/_termui_impl.py,sha256=z78J5HF_RTsOBhjNLjoigaqRap3P2pWwEDDAjoZzgUg,23452 +click/_textwrap.py,sha256=10fQ64OcBUMuK7mFvh8363_uoOxPlRItZBmKzRJDgoY,1353 +click/_unicodefun.py,sha256=JKSh1oSwG_zbjAu4TBCa9tQde2P9FiYcf4MBfy5NdT8,3201 +click/_winconsole.py,sha256=5ju3jQkcZD0W27WEMGqmEP4y_crUVzPCqsX_FYb7BO0,7860 +click/core.py,sha256=k4PA2z0BT_dmed9I52Q2VLi8r6ekTMCtCQzw2y915Xs,111478 +click/decorators.py,sha256=sGkXJGmP7eLtjtmPl_Un2uBTlrhK8s2L22n-yBiDwTw,14864 +click/exceptions.py,sha256=7gDaLGuFZBeCNwY9ERMsF2-Z3R9Fvq09Zc6IZSKjseo,9167 +click/formatting.py,sha256=Frf0-5W33-loyY_i9qrwXR8-STnW3m5gvyxLVUdyxyk,9706 +click/globals.py,sha256=kGPzxq55Ug4dFUrgRV-5oHVPOPdLCUhmYolbrrVBo8g,1985 +click/parser.py,sha256=cAEt1uQR8gq3-S9ysqbVU-fdAZNvilxw4ReJ_T1OQMk,19044 +click/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +click/shell_completion.py,sha256=_hPI12T9Ex-y5a3WunWnlH0Gca2_urzXFXkDnt7G6Ow,18001 +click/termui.py,sha256=Rp2gFE8x7j8sEIoFMOcPmuqxJQVWWTrwEzyC14-sPAw,29006 +click/testing.py,sha256=kLR5Qcny1OlgxaGB3gweTr0gQe1yVlmgQRn2esA2Fz4,16020 +click/types.py,sha256=VoNZnIlRBAtRRgzavdqVnyfzY5y4U4qzVGI1UvvX1ls,35391 +click/utils.py,sha256=avYwX-3l2KkdJNUo8NmncZSoAdEmniQ_M5sdsSYloJ4,18759 diff --git a/flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/WHEEL b/flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/WHEEL new file mode 100644 index 000000000..5bad85fdc --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: true +Tag: py3-none-any + diff --git a/flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/top_level.txt b/flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/top_level.txt new file mode 100644 index 000000000..dca9a9096 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click-8.0.3.dist-info/top_level.txt @@ -0,0 +1 @@ +click diff --git a/flask-app/venv/lib/python3.8/site-packages/click/__init__.py b/flask-app/venv/lib/python3.8/site-packages/click/__init__.py new file mode 100644 index 000000000..a2ed5d135 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click/__init__.py @@ -0,0 +1,75 @@ +""" +Click is a simple Python module inspired by the stdlib optparse to make +writing command line scripts fun. Unlike other modules, it's based +around a simple API that does not come with too much magic and is +composable. +""" +from .core import Argument as Argument +from .core import BaseCommand as BaseCommand +from .core import Command as Command +from .core import CommandCollection as CommandCollection +from .core import Context as Context +from .core import Group as Group +from .core import MultiCommand as MultiCommand +from .core import Option as Option +from .core import Parameter as Parameter +from .decorators import argument as argument +from .decorators import command as command +from .decorators import confirmation_option as confirmation_option +from .decorators import group as group +from .decorators import help_option as help_option +from .decorators import make_pass_decorator as make_pass_decorator +from .decorators import option as option +from .decorators import pass_context as pass_context +from .decorators import pass_obj as pass_obj +from .decorators import password_option as password_option +from .decorators import version_option as version_option +from .exceptions import Abort as Abort +from .exceptions import BadArgumentUsage as BadArgumentUsage +from .exceptions import BadOptionUsage as BadOptionUsage +from .exceptions import BadParameter as BadParameter +from .exceptions import ClickException as ClickException +from .exceptions import FileError as FileError +from .exceptions import MissingParameter as MissingParameter +from .exceptions import NoSuchOption as NoSuchOption +from .exceptions import UsageError as UsageError +from .formatting import HelpFormatter as HelpFormatter +from .formatting import wrap_text as wrap_text +from .globals import get_current_context as get_current_context +from .parser import OptionParser as OptionParser +from .termui import clear as clear +from .termui import confirm as confirm +from .termui import echo_via_pager as echo_via_pager +from .termui import edit as edit +from .termui import get_terminal_size as get_terminal_size +from .termui import getchar as getchar +from .termui import launch as launch +from .termui import pause as pause +from .termui import progressbar as progressbar +from .termui import prompt as prompt +from .termui import secho as secho +from .termui import style as style +from .termui import unstyle as unstyle +from .types import BOOL as BOOL +from .types import Choice as Choice +from .types import DateTime as DateTime +from .types import File as File +from .types import FLOAT as FLOAT +from .types import FloatRange as FloatRange +from .types import INT as INT +from .types import IntRange as IntRange +from .types import ParamType as ParamType +from .types import Path as Path +from .types import STRING as STRING +from .types import Tuple as Tuple +from .types import UNPROCESSED as UNPROCESSED +from .types import UUID as UUID +from .utils import echo as echo +from .utils import format_filename as format_filename +from .utils import get_app_dir as get_app_dir +from .utils import get_binary_stream as get_binary_stream +from .utils import get_os_args as get_os_args +from .utils import get_text_stream as get_text_stream +from .utils import open_file as open_file + +__version__ = "8.0.3" diff --git a/flask-app/venv/lib/python3.8/site-packages/click/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/click/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..13b1f377ef87549491066111052c10f2ac32be9a GIT binary patch literal 2710 zcmd7U$yVD)6b4`ewy}+whcKf+2w}3#Jdb7w3DXI%xf>l>5_Tn;PL<5G=yh-VG5Yiy zdE2XA^}bhq@3jr-H)wPC`KqL`=Oux4m|(^?0)nh46+B%Lomc1L=VF-dk8%OBkW=HD2%d4&|@&h z9z|=QvB#YE?l_FIHS`2bu-`i$+)0>Zk2@dTDVSnUIG@~Um}Y-K&%g|O5gB*Jny#NdBIrJhdvggrDu*A-wmtmQmMX$gLJLjyr zYp}*%K(E6(dl9_>8|)?YCTy~o(Oa;^UO{ieHhUGl13T?oF>94r4AqUrIxTFzkmTxP~^Fx~4)*eMz!ew#YvRT5#i^9}s zMWIr7Jn)rKaO_O#uLk^Fm~N;f4rHTxf$ zpJ-(46fZ77q%X=&DBcFfuY%gy$!kLzt{vJEPfHkYt2UdjD|VZ{cQ2%C&=?(ySY0Sq ztj*q1vYp_qzne;{2ZmC*Wt+YP{I&8~fU@uX=m+o;3FSe4A~&eU6_@yM8Ol6XDp$Qu2j%TnMFb zSNuFAed*ue8~0ka6wh5B(fSz+aN<*is)bBGN+Zx~x7S^?L)|P(iEpR=cGv0ZWVnjr z;>S$KHmFcoc`i5Ii*1&C{XrO5&LW=`wJkw+-bs_7*{d^pcy)D-$Kv^tFHAa^I5I-} zR=DIS6^=%poL?Q>B2p)gZ-n0rkKc=?vrFnsoO!QZ)eU$19PsdKBcyAL-`svXyF8_d zZp--ZjA*&Ly#98TKfbv+KEh`7?(U5L)-JXEp3AM@ZRsF@iw11eK91RKW7c zXI+KTHr)C-zS41{D{Qt`tN0YYosskd+v90pV(Xb)X6bVJveK;UmT&5MC5rpmmh=FZ z`0S3zC`F7SPSH$}ph!~C*8oVp{u3zKLcREvAR=2S+9=v7Iw(3Rx+uCSdMJ7^Dk;0Z zSC#Ce-ggxJ6ay546hjol6eAR)7?7$~i9X}x81-rxxJ?|tU^q&?r&c`R9Vo}K0dd+= zWg#c9AwN(|QhcPCqWDBHO)*0;OEE_=Pm!U>VpL)wzQao9sAGX*kz$ErnPP=vm12!z zonnJxlVXcvn_>r}5-W!SFL#$Z_9*r#4k!*O@)SoHl@?vEe_H6jN8|+^p!mnm22woW z#?3w$GJEek7JjH?Uly_(1@G~DZuR@3Tv+mgoc>hwHa1?CbMz_(+1qR&f_Z$NgofkH u7o}W$UNp_DNe`e}>bmemsOy>FxsuqfK7sIIir?>k#fZk!4gZ6m#(x2sR?YbU literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/click/__pycache__/_compat.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/click/__pycache__/_compat.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fcc2143bb7d149802c25ec021dd71b16c688f2de GIT binary patch literal 16123 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 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/click/__pycache__/_unicodefun.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/click/__pycache__/_unicodefun.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d389c0ceb4347f14b0214a77570ad1b701844402 GIT binary patch literal 2353 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 V_+{kr_uDhc;k*II-%``R`(MvN2VVdH literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/click/__pycache__/core.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/click/__pycache__/core.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9fbf1b75236217f252ee7f9090641a7e622c1b66 GIT binary patch literal 88247 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 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/click/__pycache__/decorators.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/click/__pycache__/decorators.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..71a0db9f3e6438b79da8de03d6ce411a50480789 GIT binary patch literal 14267 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 D58D;Q literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/click/__pycache__/formatting.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/click/__pycache__/formatting.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9296504025a9f8b22408af69771e3acaff00a25e GIT binary patch literal 9417 zcma)C&vP3`cJ7`T3*ZwaSplUq+Irpoa8M#-+Mg(2nwzZ zP}7*{>3;pX`@Qe|XnZt1T^9KK;U9h-{P&AO{1-hO{djoz8Ls4iQG6tnP%;v062Det zwd^%pQk@++EoZHOdq))_w^dv#^1T~*t;f_!X2iDpGD4Yss^O4b(M?Qs-7u6EzEGqe-s6O=&GZ!mWna$MO zaT?xETR|Lbh8i`+%`l~!hZ0Tj?4_Y@)uJ%QeJSZg&9v6;q*>+K`yagZ_SM?Q*RQU= zyIi$0N4IyA%++Dq)p4CV4?YFl6mh+UD_KR+6#KF-u8L1D?pvwoTL%*NXKz^h_P&$K zzC5)0wvrF5)b2Y6^1$lLzI|Ej%E2P~{6lQMH)@i7+LNg6pNwuI! z2QqiJsnV?^v$1uV-BjpUZ%3oa>ltb_J;p!CBXqG^sR&RIX zRF_a)w@?f|v=uI{I+fECe_`+$EK^}VFiFCIXcsr}B4yXbS%)Bg+=@ebsEsqc}Zpk2w-EQ&AWU2$0Kdk-WO z;D+hDDfY{EEl_;#hW&-yp8}8Wp{GiBEMnCtkM0`^3cs8$L&Z*{O z@gwmI@qze6?EF@h56k^B&8RvR{0u# zwET@O2EzST$W+!RZ00(5YlRRXznSt*nLAx?jMz*3K!?5|qt1i>5##kB_BSvS4T85paJQLm`KV)x zOo*uZgx>*#H{PVV`RnU9HrChu`W8gAP7^tYn%fs&TVJpGX&X~e%{o?#SC5IrZC{79 z<{cuDY_+?Q0z;in7^AgWXoxyZYF;vSuM&5@t&2$1t zHCx>lCItZU0fa+RV?D-|(sa%1Xetnx<0E&XppM02(K0(tGBe@RXzC>8je$gw;G~*> z_$a@RLBN0k9@upF-tn2xgmLq#FsN_Q-X&qZn>KHS%*oj369n{S@Yu>F7&SotJ#8rZ?C%-$AbhcV`E#7yh+ zXunp$j_4paPHR(!+gdx0_VmkmU#(~#Kl(W;hTEmd>YDM2EG{7&6I5jm4YTI8b#s$^ zz0A_L;1&eCX70<>ix;3YoZmC3ORk_0OBFZ**5La`*L7ssqBU`>g;E8-u5{6Yz7>33 zyqm##IdaZ=+R}dk3UwlL^g~|4&u}F(u%ID3=W7-mZd?6X=!AuBH+10Q=D#E-_u-@~ z7w&K|^zdAS!wc&Lhqr{M@_nhMR0U7v{K+`gQ|hJA!CIj}%8$kkaBqIz>-wYF2*z4&_31dY!>B3Xp^S8Ue#W)Pf@)0oK z3iY0!biz8kBL)o}@KzX+jRt$LUb`A7L1@*~ zetS3e8$n#(^V{9D(@lXnOyZ4{tX=|iC!-J2yTKkNL_HJYpbeL})z*9C7AaSNW?a`{ z3w;e(2TH-OYZJ@EH#{u{;E0$TL){}bYJzR@eO~9>C4mn@lf-loAvH0?1k5!=uraL; z76t7Ce_;FInf&B6Gx$Rbvc6Jvvf1OaVExTX=u=}-23QG|2On}i>G>^O$={;@D2hnz zJ8&^Y4Uv-)a0gE-L|1)8JGg&0wbn&Vn{6*NPYM>7)t*X#X|y+Y8f%c7ED>B2@^EMd-?acBqSKnjOZNVS z>sc;P$H}kLghaBpFs44sgKmgnU1QWAk6dVfY+Ss^v$*MtZE=X0h3pZB)K>46kBx)| zem8E`+bYa`3L~;4C*+aU!hV$?-Yx zd;Jn#jm5tJh7sHIc<4h^wuyQ96Jd$kfc4YYNoz=@S)Pp$2BbI3D_>h|>UalW*qO(1 z_Vh(i{sPy4x%qMC47vX=W3P=db~XH7#`MoIr2cCv#`(!yU>9WR-uJU)8gM8Gt#jb& zZ)Q%QH<7?F4)^1FMiNa?GmF6QaYH!@XwyOp$K^nN1@rM6(lHn#THmk4Z>+DNgItfi z2I?%TUljX=e!*lmRN<7kS{E<@#SCt|MBq1;kEDX>OO%Tb&g!|NyKEp96=(z;69Rxtb7-8hM zFZ)+bXh=9bq(D^G*Ux@LGrHuTU0)v&U+KA~n%%HR^2$R}3FzHf{IEBxtGIheXo{qY ztQo;))FG7Be?b3u3!$`q$bBjb&55&#(jQ` z_sI`Yh_WYVtVLO|W-aocM!uEvom(!;vRpK^P!)W?bG~!j-qN_z9iaLVPnB z*=TYs*0c~16EYzztm8t=BIiJK3ccrsqLULIfm|*(tXuj65bRl(%7k>hdgL_^knR)Q zA777Y!*@>gU(sR!Do;(CU*7IdVavAAvzV7eQc(i0EzH60JwKeoDCj;e{VWhenC2Nw z0-Prd*F!r0Mtrl8hW+V1Gl}zITdamp3o$WUMwPrXWMjz`ZGpzrmO4Z-l2DP{5DM9kPc`>#Oy~}WE>=UG8ibv^0JltUCO40KctY zNNocCf0E=tkw=e2HgbBW0PvKaHZ2d3Q~OeWDv)$`IdA9EaRTb{ivGSTQGN?H;yTyD-84lPuLB5>pf&+Sj~I5Z2M z$0@XAQ#ZvuI70tY?#%979dWjERaGF3(?N_S9jYCt%Gf(MCgFP|5`oQDdlz{=@<&Xp z;^PAWMFORTg9~M{XoCPI4|*v1G?d`nu25R|k{=8$5M}5PRUn;3S#qRRhKVTMVioXj z1yL7q(s4q$G#NWMaOjP%PYFkuEn}yQk2|q@E`x>y()Sxk)}b$FP!eSR(%mr6>lji; z6_VzE{DVwVo0o*V>d}boqW8|deH#%R>prGzxH8i{wh4TOGQw`gKUVOEZ5!xYH!FWKbr$42F z0vSyi{IwEKA4dvNR^VHB={!~aOL|=~X$-~Ll&oN~C9hHMV@{!GAVo$0ftLxTP{fQ$ zNqE*AjP26^moiN4tX#Cpa^9Lpl7hmAd23dlvyKN117dXWMgGs@@J)_7&8Lowtvb{J zNiGA?CU8Ivjsv9Vm6fCe4@#b+&ZfFg2EO?r;aj7NklUQpA%5heI!GCE=gbF0b%e~6 zMb6FQ`Jvd7vX}^jOu!(}CK1XtgXUmF5sS;Id8#pEKmlo(6yk_fX-<12VTAE(n56z& zD%i=PbC!XXZ&2;Ay(s2`e2aG`hgS5k_>@H|TfHa85j}7Fn2jGU?!arAu#|FaDPMVG z@*;9(U~=?>Sqx`1G1kZvZyYR51Kkp0i%y()D#x6%BZ1Xp)3MR*t-*RIdB{;3AD&pf zr$69RRTG^V2g}Ta_R>T#7H9oTI628h@eqMCm@N>KB%FvMk43_RCNR$w*sqY|L#Ji* zdjr2*TV2c$X_Iu+YFW8fYqeE3qWem%wu2N>{-#)~sdgRJHefxIY39Ap03Jm8I!I+U zaKj+OXnjAXF<6RNp8k&7c^~pATs_PRs|3g+sZlA7G_VHwsXc6 z|E6-@l8ZBzT=1AKIoP>*GB|)G9)5@*UA(snH#lm813^w4%<1yJ4HpB^n}T^oln6%x zN$_*Z!F$P09^8$--C!4Cr_z_(HYYlTD&CWDWj(}#FywdOrqD_F0~{+h$i1BkrohBI zI9Z@mu|dRTn9TDMBu!9lN_?|mwcrvmNVM9B&yW|_Z5(|gVq+9R4vtN(Io}`eKo%E4 z*SVqPt(iQE!Ji=B%cG%MGpQxWQEpY&*AW_GFL^uz=f)7{EU-PM1CGEPbr9Au8_*y= zz(OJr0c4ESp)t4l%uLh;OTKFkb!aeyRM*(DJ(3qzAJaS%rVnwk>h^)VOLTN#nLHN@ z6U-iYhDQX8li}WF|U&pPjqu|X`@ zxDvVse^kJqB#_EmGH2nI$ujW~#)@Ie(o<+Yi8K8KUc5|-PKMfnEcc%M9>xZLI>3Jd zDCy3rC}YbV`p<{4`xJ3BhgJed1oZC#Iyi7F zvaq_3S{RaNi3VW~7uTvwnVas>(WGvpGArjsFlufv254F|{Smz?!bNCxg8Y9EE+Q2i z)|)v|>PB~{h^gfhwOSCz?UXS)0Yn)f!M`gst-s!(f_<8l?kJ4NoQ*dA8Nm(AW>^b`fQ**+SbKh1cRRkq!ncxN>` zb`mRa0Iogs&H;|}zv#7p!d`pg)O#=Od$YS~LL+r7&%T*?`{upxz4z_+i;EQp#-C4r zVZY5e&L6lqK8mpT68`b;FmZ9q4Ffwe2$aiLuxjvymX-?SRVc8M3bC+EiHb{S3z z&z<(Xa7GpW9xw4ZUViEFd0u%@d`{X6c7I(|;r&HEx8c~+YJSQ$-Z@#sllB5wA$$q! zEc2BYUV8~vt1k&Z!_UHMnO{I_qPl@z*(YBE-E;gryt~55L#KA}EyUxrY9uL2;0f`5$q z+@=bvx)Ox3VNzZhXnpPAn(sG!8Th-|JdmmfKG&mzn~a#6p`cl~)IvhDpzD_#k(^JH`> z7rxTWD4p9wB@?KS2C+2+buwl;kO$~>el-T;b|I| zX>%;hu-VO;s2=+&65Zp7XGZFEItAY$M8>(`%_hB3-%RZ{Bc|DyrajiAL7&FLAbi~& zJpp6rS=xAX%H#wfk7>81LO255Q03{i&SFlF4b0P&1(Nk-nmYm5_t``uNErm61D9zQ z#DcQZj5NhQWbnZxG{IQh(OT(l-RfC>c>6bu*#|pV9_Si4#2}YY0Vj*GRhvtQNj#s$ zz$6_@LP-%?+TTr4~M5UMt=ql`O+)7<< z)e6>Y8$wcuRCLU>BkD^Q;2?MJI^34KcM)VjyXY&a^ndY24-4?OUVZ|?A$Yetud9c^ zg$9u3g`de2;61zh;UO8pcgyf2501MN#T0E*e} zXN7&#KS)PN3+CfE>n&IT2O&oYewiu&MekupSQV+%E-FM8F z5s;c*grj8M47Fe!*9dldap&>=d7k$^umC}bZ;{l9dXQ_AAVoWJnMM>+l=X0Im=0-KT1(y??92kYSj;Ya z1|$J56kA{-DhX3|UX|E%3|1uvRppYZ99+4jD#wU3bN;d6IXLFVqUidtsqlTq)H`k}o1(u9YQULVlt)A^9@$m0HC# zBC9j0{#4#DRojE!6W!^Ry|o!>RYCrd+9Q&mM1EgwzvQQoKTtc!-dl&lJ@?Jp;poVw zQF|1l9gU71=BP)bz910Jk{3$7yvBD#0{b+a;rBA2%p5$lXyB6T33zgQ^DtK!zf={YA60mFI??L zev$?%jY7Y-n)*TFcLTK;b=UmBZ}$3Yem_Xk$Zx6Mik~jAaoBAyoJ`8Se%cRI5)DVW z*-n?FMJo!@RTU+^idK3zFmAdOr2fsw4|{&xOZ_-P8OPfmVj*e$l)*ajf&ky)7)=ll3qL=5OQ!NE{w{EL zxB-z;J>{=NNfInZNqI4fBNcG__`=c4(4CF-Zuy{X;x9&N8r@1s6->-u?Daydn#(A& z>k4R_`g$K$xu|3{O0Zf@ei;G#ohE*-wf&l#s*OQoe__pkNkxl2wdP-$^}iMtE`2eQ88CL|5?P?9QI&bY z^W13;IvfD`!sTAfT}0L!H7{KWRIn1Ik;=?ht9IrKX3Gi-Vexh`bqDsQ`GSG> zId|~3<&yf&z}fQd8>YejX<^`Onxp=uWka3M+x;jlhUHE3Z8I$mEX>jcMvDeZvdfn-SBzNz#B1II>BBoBIZPJ7sPp~eFJ<>xB!`h z1;8fc00fqu1pp9Vc2)}ceUiW3YBz<)lhb7%pLPnS3@Y*TUawt0aX$gwZV-NnT49x^Y%Kvu1;ECAZFhOTZj10Tf;VWq=N?cDD;M2yq(oy*O&jO3V56i8Pvhf%5nDSKq8!nb}l( z!NyHW-SESF5#LAfP3DlS`*)yyfJgTYj4k87@ipVBft;yxUo+wZj*(i+W%aBH?lXXME5++gI%yAjsJpfjZmj zb-B>9>(8A1>_U9AbMD#BdG*Yv;{Lh%t;P7WpI!g-Ier%VXRn>@xBF-0SnGDrEGl(= z?R>L&w#h>MW@W?FBK%$Jx^vY~hfpC2h;GfE%glP!Qb$<0XIP^SBbSxu zy6q%w_E!4OUBh$2p*_RGCOmvv+szd>ZvF7(Am;J86x;w$-Gu7EW%zUcxoSmmu9=x; zMGb*LS52|R3;O*i4l`E;z~QW*peNwm$Xt=JtaJ@Jf^1i2ccVBf=JjFbaIRHXuVI=) zS?WgS$}2PTmR_(uw$6iSqt{s(U(PcqPCkvqs1&ggj_FuON{(5yil&9{G|CP+sIi#i zZ_%tcWwU~ozNJ2iejuye>!%NL^Kg4E^A)7%0DP$tY{MLwvR$<@@XiEaZE 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 Q0@IeSy@G$_6Ze1ee`kFf+LZURXjA=|afF|Yv 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˾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 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/click/__pycache__/testing.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/click/__pycache__/testing.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3f77a61b5a18131bfe231975ee81cab0ff5b053a GIT binary patch literal 14873 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 M0MS0qEljWd541r%jsO4v literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/click/__pycache__/types.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/click/__pycache__/types.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e58a874b1bdcd3629a55419f25c1729302752d48 GIT binary patch literal 33109 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 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/click/__pycache__/utils.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/click/__pycache__/utils.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5004c68fc4098e2957b7a7dcadc4e79516035554 GIT binary patch literal 17803 zcmc(HTZ|l6dS2c7GR@|2IHbgjZne~fY%STOv|24SRD^>oh+<;_!i*wv@1&iT(d|M~CdKYVZZ?uvoW|N8J(y*Doz#((3@=w|{q zXYh@`XBmcX_-1I#>tA!;lzVI5#;+AR?ZSM)jqge=>L~ zc*@^@&+;GhAOF~ye>!+Fc+x+B`-A==+&{zGsBsuip75W<6W4zWZN7wOPv5isXMFeL z!u*jp4gX92k&g}k$X#pxS=>GAKZm>LaQB>l2{4`~oI@NHOD~{w6o0%sijkQ9(R)Sz zm|p{|WB!YHUXw9>**}gRU-Z8r&z=vS${^_9FZ=a-j(^fWg;vM?w>cAmI9f~h@`C^J zy^{YG{}t3cfjV_U^1ph|^k4PAh8|A(m+|xz+P@SSt1mCvGQOw$)0p9F{tQ}u#eWBH z3GFHWXVB~G{y)OgS1{8z{I6rCU&l

YIVN`l^5Ckg;S9M^ZwIvw(BXKaUn)^PiW$ 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 NE&h%vN?F^7{}(iUev|+J literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/click/_compat.py b/flask-app/venv/lib/python3.8/site-packages/click/_compat.py new file mode 100644 index 000000000..b9e1f0d39 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click/_compat.py @@ -0,0 +1,627 @@ +import codecs +import io +import os +import re +import sys +import typing as t +from weakref import WeakKeyDictionary + +CYGWIN = sys.platform.startswith("cygwin") +MSYS2 = sys.platform.startswith("win") and ("GCC" in sys.version) +# Determine local App Engine environment, per Google's own suggestion +APP_ENGINE = "APPENGINE_RUNTIME" in os.environ and "Development/" in os.environ.get( + "SERVER_SOFTWARE", "" +) +WIN = sys.platform.startswith("win") and not APP_ENGINE and not MSYS2 +auto_wrap_for_ansi: t.Optional[t.Callable[[t.TextIO], t.TextIO]] = None +_ansi_re = re.compile(r"\033\[[;?0-9]*[a-zA-Z]") + + +def get_filesystem_encoding() -> str: + return sys.getfilesystemencoding() or sys.getdefaultencoding() + + +def _make_text_stream( + stream: t.BinaryIO, + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if encoding is None: + encoding = get_best_encoding(stream) + if errors is None: + errors = "replace" + return _NonClosingTextIOWrapper( + stream, + encoding, + errors, + line_buffering=True, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def is_ascii_encoding(encoding: str) -> bool: + """Checks if a given encoding is ascii.""" + try: + return codecs.lookup(encoding).name == "ascii" + except LookupError: + return False + + +def get_best_encoding(stream: t.IO) -> str: + """Returns the default stream encoding if not found.""" + rv = getattr(stream, "encoding", None) or sys.getdefaultencoding() + if is_ascii_encoding(rv): + return "utf-8" + return rv + + +class _NonClosingTextIOWrapper(io.TextIOWrapper): + def __init__( + self, + stream: t.BinaryIO, + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, + force_writable: bool = False, + **extra: t.Any, + ) -> None: + self._stream = stream = t.cast( + t.BinaryIO, _FixupStream(stream, force_readable, force_writable) + ) + super().__init__(stream, encoding, errors, **extra) + + def __del__(self) -> None: + try: + self.detach() + except Exception: + pass + + def isatty(self) -> bool: + # https://bitbucket.org/pypy/pypy/issue/1803 + return self._stream.isatty() + + +class _FixupStream: + """The new io interface needs more from streams than streams + traditionally implement. As such, this fix-up code is necessary in + some circumstances. + + The forcing of readable and writable flags are there because some tools + put badly patched objects on sys (one such offender are certain version + of jupyter notebook). + """ + + def __init__( + self, + stream: t.BinaryIO, + force_readable: bool = False, + force_writable: bool = False, + ): + self._stream = stream + self._force_readable = force_readable + self._force_writable = force_writable + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._stream, name) + + def read1(self, size: int) -> bytes: + f = getattr(self._stream, "read1", None) + + if f is not None: + return t.cast(bytes, f(size)) + + return self._stream.read(size) + + def readable(self) -> bool: + if self._force_readable: + return True + x = getattr(self._stream, "readable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.read(0) + except Exception: + return False + return True + + def writable(self) -> bool: + if self._force_writable: + return True + x = getattr(self._stream, "writable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.write("") # type: ignore + except Exception: + try: + self._stream.write(b"") + except Exception: + return False + return True + + def seekable(self) -> bool: + x = getattr(self._stream, "seekable", None) + if x is not None: + return t.cast(bool, x()) + try: + self._stream.seek(self._stream.tell()) + except Exception: + return False + return True + + +def _is_binary_reader(stream: t.IO, default: bool = False) -> bool: + try: + return isinstance(stream.read(0), bytes) + except Exception: + return default + # This happens in some cases where the stream was already + # closed. In this case, we assume the default. + + +def _is_binary_writer(stream: t.IO, default: bool = False) -> bool: + try: + stream.write(b"") + except Exception: + try: + stream.write("") + return False + except Exception: + pass + return default + return True + + +def _find_binary_reader(stream: t.IO) -> t.Optional[t.BinaryIO]: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_reader(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_reader(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _find_binary_writer(stream: t.IO) -> t.Optional[t.BinaryIO]: + # We need to figure out if the given stream is already binary. + # This can happen because the official docs recommend detaching + # the streams to get binary streams. Some code might do this, so + # we need to deal with this case explicitly. + if _is_binary_writer(stream, False): + return t.cast(t.BinaryIO, stream) + + buf = getattr(stream, "buffer", None) + + # Same situation here; this time we assume that the buffer is + # actually binary in case it's closed. + if buf is not None and _is_binary_writer(buf, True): + return t.cast(t.BinaryIO, buf) + + return None + + +def _stream_is_misconfigured(stream: t.TextIO) -> bool: + """A stream is misconfigured if its encoding is ASCII.""" + # If the stream does not have an encoding set, we assume it's set + # to ASCII. This appears to happen in certain unittest + # environments. It's not quite clear what the correct behavior is + # but this at least will force Click to recover somehow. + return is_ascii_encoding(getattr(stream, "encoding", None) or "ascii") + + +def _is_compat_stream_attr(stream: t.TextIO, attr: str, value: t.Optional[str]) -> bool: + """A stream attribute is compatible if it is equal to the + desired value or the desired value is unset and the attribute + has a value. + """ + stream_value = getattr(stream, attr, None) + return stream_value == value or (value is None and stream_value is not None) + + +def _is_compatible_text_stream( + stream: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] +) -> bool: + """Check if a stream's encoding and errors attributes are + compatible with the desired values. + """ + return _is_compat_stream_attr( + stream, "encoding", encoding + ) and _is_compat_stream_attr(stream, "errors", errors) + + +def _force_correct_text_stream( + text_stream: t.IO, + encoding: t.Optional[str], + errors: t.Optional[str], + is_binary: t.Callable[[t.IO, bool], bool], + find_binary: t.Callable[[t.IO], t.Optional[t.BinaryIO]], + force_readable: bool = False, + force_writable: bool = False, +) -> t.TextIO: + if is_binary(text_stream, False): + binary_reader = t.cast(t.BinaryIO, text_stream) + else: + text_stream = t.cast(t.TextIO, text_stream) + # If the stream looks compatible, and won't default to a + # misconfigured ascii encoding, return it as-is. + if _is_compatible_text_stream(text_stream, encoding, errors) and not ( + encoding is None and _stream_is_misconfigured(text_stream) + ): + return text_stream + + # Otherwise, get the underlying binary reader. + possible_binary_reader = find_binary(text_stream) + + # If that's not possible, silently use the original reader + # and get mojibake instead of exceptions. + if possible_binary_reader is None: + return text_stream + + binary_reader = possible_binary_reader + + # Default errors to replace instead of strict in order to get + # something that works. + if errors is None: + errors = "replace" + + # Wrap the binary stream in a text stream with the correct + # encoding parameters. + return _make_text_stream( + binary_reader, + encoding, + errors, + force_readable=force_readable, + force_writable=force_writable, + ) + + +def _force_correct_text_reader( + text_reader: t.IO, + encoding: t.Optional[str], + errors: t.Optional[str], + force_readable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_reader, + encoding, + errors, + _is_binary_reader, + _find_binary_reader, + force_readable=force_readable, + ) + + +def _force_correct_text_writer( + text_writer: t.IO, + encoding: t.Optional[str], + errors: t.Optional[str], + force_writable: bool = False, +) -> t.TextIO: + return _force_correct_text_stream( + text_writer, + encoding, + errors, + _is_binary_writer, + _find_binary_writer, + force_writable=force_writable, + ) + + +def get_binary_stdin() -> t.BinaryIO: + reader = _find_binary_reader(sys.stdin) + if reader is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdin.") + return reader + + +def get_binary_stdout() -> t.BinaryIO: + writer = _find_binary_writer(sys.stdout) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stdout.") + return writer + + +def get_binary_stderr() -> t.BinaryIO: + writer = _find_binary_writer(sys.stderr) + if writer is None: + raise RuntimeError("Was not able to determine binary stream for sys.stderr.") + return writer + + +def get_text_stdin( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdin, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_reader(sys.stdin, encoding, errors, force_readable=True) + + +def get_text_stdout( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stdout, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stdout, encoding, errors, force_writable=True) + + +def get_text_stderr( + encoding: t.Optional[str] = None, errors: t.Optional[str] = None +) -> t.TextIO: + rv = _get_windows_console_stream(sys.stderr, encoding, errors) + if rv is not None: + return rv + return _force_correct_text_writer(sys.stderr, encoding, errors, force_writable=True) + + +def _wrap_io_open( + file: t.Union[str, os.PathLike, int], + mode: str, + encoding: t.Optional[str], + errors: t.Optional[str], +) -> t.IO: + """Handles not passing ``encoding`` and ``errors`` in binary mode.""" + if "b" in mode: + return open(file, mode) + + return open(file, mode, encoding=encoding, errors=errors) + + +def open_stream( + filename: str, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + atomic: bool = False, +) -> t.Tuple[t.IO, bool]: + binary = "b" in mode + + # Standard streams first. These are simple because they don't need + # special handling for the atomic flag. It's entirely ignored. + if filename == "-": + if any(m in mode for m in ["w", "a", "x"]): + if binary: + return get_binary_stdout(), False + return get_text_stdout(encoding=encoding, errors=errors), False + if binary: + return get_binary_stdin(), False + return get_text_stdin(encoding=encoding, errors=errors), False + + # Non-atomic writes directly go out through the regular open functions. + if not atomic: + return _wrap_io_open(filename, mode, encoding, errors), True + + # Some usability stuff for atomic writes + if "a" in mode: + raise ValueError( + "Appending to an existing file is not supported, because that" + " would involve an expensive `copy`-operation to a temporary" + " file. Open the file in normal `w`-mode and copy explicitly" + " if that's what you're after." + ) + if "x" in mode: + raise ValueError("Use the `overwrite`-parameter instead.") + if "w" not in mode: + raise ValueError("Atomic writes only make sense with `w`-mode.") + + # Atomic writes are more complicated. They work by opening a file + # as a proxy in the same folder and then using the fdopen + # functionality to wrap it in a Python file. Then we wrap it in an + # atomic file that moves the file over on close. + import errno + import random + + try: + perm: t.Optional[int] = os.stat(filename).st_mode + except OSError: + perm = None + + flags = os.O_RDWR | os.O_CREAT | os.O_EXCL + + if binary: + flags |= getattr(os, "O_BINARY", 0) + + while True: + tmp_filename = os.path.join( + os.path.dirname(filename), + f".__atomic-write{random.randrange(1 << 32):08x}", + ) + try: + fd = os.open(tmp_filename, flags, 0o666 if perm is None else perm) + break + except OSError as e: + if e.errno == errno.EEXIST or ( + os.name == "nt" + and e.errno == errno.EACCES + and os.path.isdir(e.filename) + and os.access(e.filename, os.W_OK) + ): + continue + raise + + if perm is not None: + os.chmod(tmp_filename, perm) # in case perm includes bits in umask + + f = _wrap_io_open(fd, mode, encoding, errors) + af = _AtomicFile(f, tmp_filename, os.path.realpath(filename)) + return t.cast(t.IO, af), True + + +class _AtomicFile: + def __init__(self, f: t.IO, tmp_filename: str, real_filename: str) -> None: + self._f = f + self._tmp_filename = tmp_filename + self._real_filename = real_filename + self.closed = False + + @property + def name(self) -> str: + return self._real_filename + + def close(self, delete: bool = False) -> None: + if self.closed: + return + self._f.close() + os.replace(self._tmp_filename, self._real_filename) + self.closed = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._f, name) + + def __enter__(self) -> "_AtomicFile": + return self + + def __exit__(self, exc_type, exc_value, tb): # type: ignore + self.close(delete=exc_type is not None) + + def __repr__(self) -> str: + return repr(self._f) + + +def strip_ansi(value: str) -> str: + return _ansi_re.sub("", value) + + +def _is_jupyter_kernel_output(stream: t.IO) -> bool: + while isinstance(stream, (_FixupStream, _NonClosingTextIOWrapper)): + stream = stream._stream + + return stream.__class__.__module__.startswith("ipykernel.") + + +def should_strip_ansi( + stream: t.Optional[t.IO] = None, color: t.Optional[bool] = None +) -> bool: + if color is None: + if stream is None: + stream = sys.stdin + return not isatty(stream) and not _is_jupyter_kernel_output(stream) + return not color + + +# On Windows, wrap the output streams with colorama to support ANSI +# color codes. +# NOTE: double check is needed so mypy does not analyze this on Linux +if sys.platform.startswith("win") and WIN: + from ._winconsole import _get_windows_console_stream + + def _get_argv_encoding() -> str: + import locale + + return locale.getpreferredencoding() + + _ansi_stream_wrappers: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def auto_wrap_for_ansi( + stream: t.TextIO, color: t.Optional[bool] = None + ) -> t.TextIO: + """Support ANSI color and style codes on Windows by wrapping a + stream with colorama. + """ + try: + cached = _ansi_stream_wrappers.get(stream) + except Exception: + cached = None + + if cached is not None: + return cached + + import colorama + + strip = should_strip_ansi(stream, color) + ansi_wrapper = colorama.AnsiToWin32(stream, strip=strip) + rv = t.cast(t.TextIO, ansi_wrapper.stream) + _write = rv.write + + def _safe_write(s): + try: + return _write(s) + except BaseException: + ansi_wrapper.reset_all() + raise + + rv.write = _safe_write + + try: + _ansi_stream_wrappers[stream] = rv + except Exception: + pass + + return rv + + +else: + + def _get_argv_encoding() -> str: + return getattr(sys.stdin, "encoding", None) or get_filesystem_encoding() + + def _get_windows_console_stream( + f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] + ) -> t.Optional[t.TextIO]: + return None + + +def term_len(x: str) -> int: + return len(strip_ansi(x)) + + +def isatty(stream: t.IO) -> bool: + try: + return stream.isatty() + except Exception: + return False + + +def _make_cached_stream_func( + src_func: t.Callable[[], t.TextIO], wrapper_func: t.Callable[[], t.TextIO] +) -> t.Callable[[], t.TextIO]: + cache: t.MutableMapping[t.TextIO, t.TextIO] = WeakKeyDictionary() + + def func() -> t.TextIO: + stream = src_func() + try: + rv = cache.get(stream) + except Exception: + rv = None + if rv is not None: + return rv + rv = wrapper_func() + try: + cache[stream] = rv + except Exception: + pass + return rv + + return func + + +_default_text_stdin = _make_cached_stream_func(lambda: sys.stdin, get_text_stdin) +_default_text_stdout = _make_cached_stream_func(lambda: sys.stdout, get_text_stdout) +_default_text_stderr = _make_cached_stream_func(lambda: sys.stderr, get_text_stderr) + + +binary_streams: t.Mapping[str, t.Callable[[], t.BinaryIO]] = { + "stdin": get_binary_stdin, + "stdout": get_binary_stdout, + "stderr": get_binary_stderr, +} + +text_streams: t.Mapping[ + str, t.Callable[[t.Optional[str], t.Optional[str]], t.TextIO] +] = { + "stdin": get_text_stdin, + "stdout": get_text_stdout, + "stderr": get_text_stderr, +} diff --git a/flask-app/venv/lib/python3.8/site-packages/click/_termui_impl.py b/flask-app/venv/lib/python3.8/site-packages/click/_termui_impl.py new file mode 100644 index 000000000..39c1d08f4 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click/_termui_impl.py @@ -0,0 +1,718 @@ +""" +This module contains implementations for the termui module. To keep the +import time of Click down, some infrequently used functionality is +placed in this module and only imported as needed. +""" +import contextlib +import math +import os +import sys +import time +import typing as t +from gettext import gettext as _ + +from ._compat import _default_text_stdout +from ._compat import CYGWIN +from ._compat import get_best_encoding +from ._compat import isatty +from ._compat import open_stream +from ._compat import strip_ansi +from ._compat import term_len +from ._compat import WIN +from .exceptions import ClickException +from .utils import echo + +V = t.TypeVar("V") + +if os.name == "nt": + BEFORE_BAR = "\r" + AFTER_BAR = "\n" +else: + BEFORE_BAR = "\r\033[?25l" + AFTER_BAR = "\033[?25h\n" + + +class ProgressBar(t.Generic[V]): + def __init__( + self, + iterable: t.Optional[t.Iterable[V]], + length: t.Optional[int] = None, + fill_char: str = "#", + empty_char: str = " ", + bar_template: str = "%(bar)s", + info_sep: str = " ", + show_eta: bool = True, + show_percent: t.Optional[bool] = None, + show_pos: bool = False, + item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, + label: t.Optional[str] = None, + file: t.Optional[t.TextIO] = None, + color: t.Optional[bool] = None, + update_min_steps: int = 1, + width: int = 30, + ) -> None: + self.fill_char = fill_char + self.empty_char = empty_char + self.bar_template = bar_template + self.info_sep = info_sep + self.show_eta = show_eta + self.show_percent = show_percent + self.show_pos = show_pos + self.item_show_func = item_show_func + self.label = label or "" + if file is None: + file = _default_text_stdout() + self.file = file + self.color = color + self.update_min_steps = update_min_steps + self._completed_intervals = 0 + self.width = width + self.autowidth = width == 0 + + if length is None: + from operator import length_hint + + length = length_hint(iterable, -1) + + if length == -1: + length = None + if iterable is None: + if length is None: + raise TypeError("iterable or length is required") + iterable = t.cast(t.Iterable[V], range(length)) + self.iter = iter(iterable) + self.length = length + self.pos = 0 + self.avg: t.List[float] = [] + self.start = self.last_eta = time.time() + self.eta_known = False + self.finished = False + self.max_width: t.Optional[int] = None + self.entered = False + self.current_item: t.Optional[V] = None + self.is_hidden = not isatty(self.file) + self._last_line: t.Optional[str] = None + + def __enter__(self) -> "ProgressBar": + self.entered = True + self.render_progress() + return self + + def __exit__(self, exc_type, exc_value, tb): # type: ignore + self.render_finish() + + def __iter__(self) -> t.Iterator[V]: + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + self.render_progress() + return self.generator() + + def __next__(self) -> V: + # Iteration is defined in terms of a generator function, + # returned by iter(self); use that to define next(). This works + # because `self.iter` is an iterable consumed by that generator, + # so it is re-entry safe. Calling `next(self.generator())` + # twice works and does "what you want". + return next(iter(self)) + + def render_finish(self) -> None: + if self.is_hidden: + return + self.file.write(AFTER_BAR) + self.file.flush() + + @property + def pct(self) -> float: + if self.finished: + return 1.0 + return min(self.pos / (float(self.length or 1) or 1), 1.0) + + @property + def time_per_iteration(self) -> float: + if not self.avg: + return 0.0 + return sum(self.avg) / float(len(self.avg)) + + @property + def eta(self) -> float: + if self.length is not None and not self.finished: + return self.time_per_iteration * (self.length - self.pos) + return 0.0 + + def format_eta(self) -> str: + if self.eta_known: + t = int(self.eta) + seconds = t % 60 + t //= 60 + minutes = t % 60 + t //= 60 + hours = t % 24 + t //= 24 + if t > 0: + return f"{t}d {hours:02}:{minutes:02}:{seconds:02}" + else: + return f"{hours:02}:{minutes:02}:{seconds:02}" + return "" + + def format_pos(self) -> str: + pos = str(self.pos) + if self.length is not None: + pos += f"/{self.length}" + return pos + + def format_pct(self) -> str: + return f"{int(self.pct * 100): 4}%"[1:] + + def format_bar(self) -> str: + if self.length is not None: + bar_length = int(self.pct * self.width) + bar = self.fill_char * bar_length + bar += self.empty_char * (self.width - bar_length) + elif self.finished: + bar = self.fill_char * self.width + else: + chars = list(self.empty_char * (self.width or 1)) + if self.time_per_iteration != 0: + chars[ + int( + (math.cos(self.pos * self.time_per_iteration) / 2.0 + 0.5) + * self.width + ) + ] = self.fill_char + bar = "".join(chars) + return bar + + def format_progress_line(self) -> str: + show_percent = self.show_percent + + info_bits = [] + if self.length is not None and show_percent is None: + show_percent = not self.show_pos + + if self.show_pos: + info_bits.append(self.format_pos()) + if show_percent: + info_bits.append(self.format_pct()) + if self.show_eta and self.eta_known and not self.finished: + info_bits.append(self.format_eta()) + if self.item_show_func is not None: + item_info = self.item_show_func(self.current_item) + if item_info is not None: + info_bits.append(item_info) + + return ( + self.bar_template + % { + "label": self.label, + "bar": self.format_bar(), + "info": self.info_sep.join(info_bits), + } + ).rstrip() + + def render_progress(self) -> None: + import shutil + + if self.is_hidden: + # Only output the label as it changes if the output is not a + # TTY. Use file=stderr if you expect to be piping stdout. + if self._last_line != self.label: + self._last_line = self.label + echo(self.label, file=self.file, color=self.color) + + return + + buf = [] + # Update width in case the terminal has been resized + if self.autowidth: + old_width = self.width + self.width = 0 + clutter_length = term_len(self.format_progress_line()) + new_width = max(0, shutil.get_terminal_size().columns - clutter_length) + if new_width < old_width: + buf.append(BEFORE_BAR) + buf.append(" " * self.max_width) # type: ignore + self.max_width = new_width + self.width = new_width + + clear_width = self.width + if self.max_width is not None: + clear_width = self.max_width + + buf.append(BEFORE_BAR) + line = self.format_progress_line() + line_len = term_len(line) + if self.max_width is None or self.max_width < line_len: + self.max_width = line_len + + buf.append(line) + buf.append(" " * (clear_width - line_len)) + line = "".join(buf) + # Render the line only if it changed. + + if line != self._last_line: + self._last_line = line + echo(line, file=self.file, color=self.color, nl=False) + self.file.flush() + + def make_step(self, n_steps: int) -> None: + self.pos += n_steps + if self.length is not None and self.pos >= self.length: + self.finished = True + + if (time.time() - self.last_eta) < 1.0: + return + + self.last_eta = time.time() + + # self.avg is a rolling list of length <= 7 of steps where steps are + # defined as time elapsed divided by the total progress through + # self.length. + if self.pos: + step = (time.time() - self.start) / self.pos + else: + step = time.time() - self.start + + self.avg = self.avg[-6:] + [step] + + self.eta_known = self.length is not None + + def update(self, n_steps: int, current_item: t.Optional[V] = None) -> None: + """Update the progress bar by advancing a specified number of + steps, and optionally set the ``current_item`` for this new + position. + + :param n_steps: Number of steps to advance. + :param current_item: Optional item to set as ``current_item`` + for the updated position. + + .. versionchanged:: 8.0 + Added the ``current_item`` optional parameter. + + .. versionchanged:: 8.0 + Only render when the number of steps meets the + ``update_min_steps`` threshold. + """ + if current_item is not None: + self.current_item = current_item + + self._completed_intervals += n_steps + + if self._completed_intervals >= self.update_min_steps: + self.make_step(self._completed_intervals) + self.render_progress() + self._completed_intervals = 0 + + def finish(self) -> None: + self.eta_known = False + self.current_item = None + self.finished = True + + def generator(self) -> t.Iterator[V]: + """Return a generator which yields the items added to the bar + during construction, and updates the progress bar *after* the + yielded block returns. + """ + # WARNING: the iterator interface for `ProgressBar` relies on + # this and only works because this is a simple generator which + # doesn't create or manage additional state. If this function + # changes, the impact should be evaluated both against + # `iter(bar)` and `next(bar)`. `next()` in particular may call + # `self.generator()` repeatedly, and this must remain safe in + # order for that interface to work. + if not self.entered: + raise RuntimeError("You need to use progress bars in a with block.") + + if self.is_hidden: + yield from self.iter + else: + for rv in self.iter: + self.current_item = rv + + # This allows show_item_func to be updated before the + # item is processed. Only trigger at the beginning of + # the update interval. + if self._completed_intervals == 0: + self.render_progress() + + yield rv + self.update(1) + + self.finish() + self.render_progress() + + +def pager(generator: t.Iterable[str], color: t.Optional[bool] = None) -> None: + """Decide what method to use for paging through text.""" + stdout = _default_text_stdout() + if not isatty(sys.stdin) or not isatty(stdout): + return _nullpager(stdout, generator, color) + pager_cmd = (os.environ.get("PAGER", None) or "").strip() + if pager_cmd: + if WIN: + return _tempfilepager(generator, pager_cmd, color) + return _pipepager(generator, pager_cmd, color) + if os.environ.get("TERM") in ("dumb", "emacs"): + return _nullpager(stdout, generator, color) + if WIN or sys.platform.startswith("os2"): + return _tempfilepager(generator, "more <", color) + if hasattr(os, "system") and os.system("(less) 2>/dev/null") == 0: + return _pipepager(generator, "less", color) + + import tempfile + + fd, filename = tempfile.mkstemp() + os.close(fd) + try: + if hasattr(os, "system") and os.system(f'more "{filename}"') == 0: + return _pipepager(generator, "more", color) + return _nullpager(stdout, generator, color) + finally: + os.unlink(filename) + + +def _pipepager(generator: t.Iterable[str], cmd: str, color: t.Optional[bool]) -> None: + """Page through text by feeding it to another program. Invoking a + pager through this might support colors. + """ + import subprocess + + env = dict(os.environ) + + # If we're piping to less we might support colors under the + # condition that + cmd_detail = cmd.rsplit("/", 1)[-1].split() + if color is None and cmd_detail[0] == "less": + less_flags = f"{os.environ.get('LESS', '')}{' '.join(cmd_detail[1:])}" + if not less_flags: + env["LESS"] = "-R" + color = True + elif "r" in less_flags or "R" in less_flags: + color = True + + c = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, env=env) + stdin = t.cast(t.BinaryIO, c.stdin) + encoding = get_best_encoding(stdin) + try: + for text in generator: + if not color: + text = strip_ansi(text) + + stdin.write(text.encode(encoding, "replace")) + except (OSError, KeyboardInterrupt): + pass + else: + stdin.close() + + # Less doesn't respect ^C, but catches it for its own UI purposes (aborting + # search or other commands inside less). + # + # That means when the user hits ^C, the parent process (click) terminates, + # but less is still alive, paging the output and messing up the terminal. + # + # If the user wants to make the pager exit on ^C, they should set + # `LESS='-K'`. It's not our decision to make. + while True: + try: + c.wait() + except KeyboardInterrupt: + pass + else: + break + + +def _tempfilepager( + generator: t.Iterable[str], cmd: str, color: t.Optional[bool] +) -> None: + """Page through text by invoking a program on a temporary file.""" + import tempfile + + fd, filename = tempfile.mkstemp() + # TODO: This never terminates if the passed generator never terminates. + text = "".join(generator) + if not color: + text = strip_ansi(text) + encoding = get_best_encoding(sys.stdout) + with open_stream(filename, "wb")[0] as f: + f.write(text.encode(encoding)) + try: + os.system(f'{cmd} "{filename}"') + finally: + os.close(fd) + os.unlink(filename) + + +def _nullpager( + stream: t.TextIO, generator: t.Iterable[str], color: t.Optional[bool] +) -> None: + """Simply print unformatted text. This is the ultimate fallback.""" + for text in generator: + if not color: + text = strip_ansi(text) + stream.write(text) + + +class Editor: + def __init__( + self, + editor: t.Optional[str] = None, + env: t.Optional[t.Mapping[str, str]] = None, + require_save: bool = True, + extension: str = ".txt", + ) -> None: + self.editor = editor + self.env = env + self.require_save = require_save + self.extension = extension + + def get_editor(self) -> str: + if self.editor is not None: + return self.editor + for key in "VISUAL", "EDITOR": + rv = os.environ.get(key) + if rv: + return rv + if WIN: + return "notepad" + for editor in "sensible-editor", "vim", "nano": + if os.system(f"which {editor} >/dev/null 2>&1") == 0: + return editor + return "vi" + + def edit_file(self, filename: str) -> None: + import subprocess + + editor = self.get_editor() + environ: t.Optional[t.Dict[str, str]] = None + + if self.env: + environ = os.environ.copy() + environ.update(self.env) + + try: + c = subprocess.Popen(f'{editor} "{filename}"', env=environ, shell=True) + exit_code = c.wait() + if exit_code != 0: + raise ClickException( + _("{editor}: Editing failed").format(editor=editor) + ) + except OSError as e: + raise ClickException( + _("{editor}: Editing failed: {e}").format(editor=editor, e=e) + ) from e + + def edit(self, text: t.Optional[t.AnyStr]) -> t.Optional[t.AnyStr]: + import tempfile + + if not text: + data = b"" + elif isinstance(text, (bytes, bytearray)): + data = text + else: + if text and not text.endswith("\n"): + text += "\n" + + if WIN: + data = text.replace("\n", "\r\n").encode("utf-8-sig") + else: + data = text.encode("utf-8") + + fd, name = tempfile.mkstemp(prefix="editor-", suffix=self.extension) + f: t.BinaryIO + + try: + with os.fdopen(fd, "wb") as f: + f.write(data) + + # If the filesystem resolution is 1 second, like Mac OS + # 10.12 Extended, or 2 seconds, like FAT32, and the editor + # closes very fast, require_save can fail. Set the modified + # time to be 2 seconds in the past to work around this. + os.utime(name, (os.path.getatime(name), os.path.getmtime(name) - 2)) + # Depending on the resolution, the exact value might not be + # recorded, so get the new recorded value. + timestamp = os.path.getmtime(name) + + self.edit_file(name) + + if self.require_save and os.path.getmtime(name) == timestamp: + return None + + with open(name, "rb") as f: + rv = f.read() + + if isinstance(text, (bytes, bytearray)): + return rv + + return rv.decode("utf-8-sig").replace("\r\n", "\n") # type: ignore + finally: + os.unlink(name) + + +def open_url(url: str, wait: bool = False, locate: bool = False) -> int: + import subprocess + + def _unquote_file(url: str) -> str: + from urllib.parse import unquote + + if url.startswith("file://"): + url = unquote(url[7:]) + + return url + + if sys.platform == "darwin": + args = ["open"] + if wait: + args.append("-W") + if locate: + args.append("-R") + args.append(_unquote_file(url)) + null = open("/dev/null", "w") + try: + return subprocess.Popen(args, stderr=null).wait() + finally: + null.close() + elif WIN: + if locate: + url = _unquote_file(url.replace('"', "")) + args = f'explorer /select,"{url}"' + else: + url = url.replace('"', "") + wait_str = "/WAIT" if wait else "" + args = f'start {wait_str} "" "{url}"' + return os.system(args) + elif CYGWIN: + if locate: + url = os.path.dirname(_unquote_file(url).replace('"', "")) + args = f'cygstart "{url}"' + else: + url = url.replace('"', "") + wait_str = "-w" if wait else "" + args = f'cygstart {wait_str} "{url}"' + return os.system(args) + + try: + if locate: + url = os.path.dirname(_unquote_file(url)) or "." + else: + url = _unquote_file(url) + c = subprocess.Popen(["xdg-open", url]) + if wait: + return c.wait() + return 0 + except OSError: + if url.startswith(("http://", "https://")) and not locate and not wait: + import webbrowser + + webbrowser.open(url) + return 0 + return 1 + + +def _translate_ch_to_exc(ch: str) -> t.Optional[BaseException]: + if ch == "\x03": + raise KeyboardInterrupt() + + if ch == "\x04" and not WIN: # Unix-like, Ctrl+D + raise EOFError() + + if ch == "\x1a" and WIN: # Windows, Ctrl+Z + raise EOFError() + + return None + + +if WIN: + import msvcrt + + @contextlib.contextmanager + def raw_terminal() -> t.Iterator[int]: + yield -1 + + def getchar(echo: bool) -> str: + # The function `getch` will return a bytes object corresponding to + # the pressed character. Since Windows 10 build 1803, it will also + # return \x00 when called a second time after pressing a regular key. + # + # `getwch` does not share this probably-bugged behavior. Moreover, it + # returns a Unicode object by default, which is what we want. + # + # Either of these functions will return \x00 or \xe0 to indicate + # a special key, and you need to call the same function again to get + # the "rest" of the code. The fun part is that \u00e0 is + # "latin small letter a with grave", so if you type that on a French + # keyboard, you _also_ get a \xe0. + # E.g., consider the Up arrow. This returns \xe0 and then \x48. The + # resulting Unicode string reads as "a with grave" + "capital H". + # This is indistinguishable from when the user actually types + # "a with grave" and then "capital H". + # + # When \xe0 is returned, we assume it's part of a special-key sequence + # and call `getwch` again, but that means that when the user types + # the \u00e0 character, `getchar` doesn't return until a second + # character is typed. + # The alternative is returning immediately, but that would mess up + # cross-platform handling of arrow keys and others that start with + # \xe0. Another option is using `getch`, but then we can't reliably + # read non-ASCII characters, because return values of `getch` are + # limited to the current 8-bit codepage. + # + # Anyway, Click doesn't claim to do this Right(tm), and using `getwch` + # is doing the right thing in more situations than with `getch`. + func: t.Callable[[], str] + + if echo: + func = msvcrt.getwche # type: ignore + else: + func = msvcrt.getwch # type: ignore + + rv = func() + + if rv in ("\x00", "\xe0"): + # \x00 and \xe0 are control characters that indicate special key, + # see above. + rv += func() + + _translate_ch_to_exc(rv) + return rv + + +else: + import tty + import termios + + @contextlib.contextmanager + def raw_terminal() -> t.Iterator[int]: + f: t.Optional[t.TextIO] + fd: int + + if not isatty(sys.stdin): + f = open("/dev/tty") + fd = f.fileno() + else: + fd = sys.stdin.fileno() + f = None + + try: + old_settings = termios.tcgetattr(fd) + + try: + tty.setraw(fd) + yield fd + finally: + termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) + sys.stdout.flush() + + if f is not None: + f.close() + except termios.error: + pass + + def getchar(echo: bool) -> str: + with raw_terminal() as fd: + ch = os.read(fd, 32).decode(get_best_encoding(sys.stdin), "replace") + + if echo and isatty(sys.stdout): + sys.stdout.write(ch) + + _translate_ch_to_exc(ch) + return ch diff --git a/flask-app/venv/lib/python3.8/site-packages/click/_textwrap.py b/flask-app/venv/lib/python3.8/site-packages/click/_textwrap.py new file mode 100644 index 000000000..b47dcbd42 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click/_textwrap.py @@ -0,0 +1,49 @@ +import textwrap +import typing as t +from contextlib import contextmanager + + +class TextWrapper(textwrap.TextWrapper): + def _handle_long_word( + self, + reversed_chunks: t.List[str], + cur_line: t.List[str], + cur_len: int, + width: int, + ) -> None: + space_left = max(width - cur_len, 1) + + if self.break_long_words: + last = reversed_chunks[-1] + cut = last[:space_left] + res = last[space_left:] + cur_line.append(cut) + reversed_chunks[-1] = res + elif not cur_line: + cur_line.append(reversed_chunks.pop()) + + @contextmanager + def extra_indent(self, indent: str) -> t.Iterator[None]: + old_initial_indent = self.initial_indent + old_subsequent_indent = self.subsequent_indent + self.initial_indent += indent + self.subsequent_indent += indent + + try: + yield + finally: + self.initial_indent = old_initial_indent + self.subsequent_indent = old_subsequent_indent + + def indent_only(self, text: str) -> str: + rv = [] + + for idx, line in enumerate(text.splitlines()): + indent = self.initial_indent + + if idx > 0: + indent = self.subsequent_indent + + rv.append(f"{indent}{line}") + + return "\n".join(rv) diff --git a/flask-app/venv/lib/python3.8/site-packages/click/_unicodefun.py b/flask-app/venv/lib/python3.8/site-packages/click/_unicodefun.py new file mode 100644 index 000000000..9cb30c384 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click/_unicodefun.py @@ -0,0 +1,100 @@ +import codecs +import os +from gettext import gettext as _ + + +def _verify_python_env() -> None: + """Ensures that the environment is good for Unicode.""" + try: + from locale import getpreferredencoding + + fs_enc = codecs.lookup(getpreferredencoding()).name + except Exception: + fs_enc = "ascii" + + if fs_enc != "ascii": + return + + extra = [ + _( + "Click will abort further execution because Python was" + " configured to use ASCII as encoding for the environment." + " Consult https://click.palletsprojects.com/unicode-support/" + " for mitigation steps." + ) + ] + + if os.name == "posix": + import subprocess + + try: + rv = subprocess.Popen( + ["locale", "-a"], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + encoding="ascii", + errors="replace", + ).communicate()[0] + except OSError: + rv = "" + + good_locales = set() + has_c_utf8 = False + + for line in rv.splitlines(): + locale = line.strip() + + if locale.lower().endswith((".utf-8", ".utf8")): + good_locales.add(locale) + + if locale.lower() in ("c.utf8", "c.utf-8"): + has_c_utf8 = True + + if not good_locales: + extra.append( + _( + "Additional information: on this system no suitable" + " UTF-8 locales were discovered. This most likely" + " requires resolving by reconfiguring the locale" + " system." + ) + ) + elif has_c_utf8: + extra.append( + _( + "This system supports the C.UTF-8 locale which is" + " recommended. You might be able to resolve your" + " issue by exporting the following environment" + " variables:" + ) + ) + extra.append(" export LC_ALL=C.UTF-8\n export LANG=C.UTF-8") + else: + extra.append( + _( + "This system lists some UTF-8 supporting locales" + " that you can pick from. The following suitable" + " locales were discovered: {locales}" + ).format(locales=", ".join(sorted(good_locales))) + ) + + bad_locale = None + + for env_locale in os.environ.get("LC_ALL"), os.environ.get("LANG"): + if env_locale and env_locale.lower().endswith((".utf-8", ".utf8")): + bad_locale = env_locale + + if env_locale is not None: + break + + if bad_locale is not None: + extra.append( + _( + "Click discovered that you exported a UTF-8 locale" + " but the locale system could not pick up from it" + " because it does not exist. The exported locale is" + " {locale!r} but it is not supported." + ).format(locale=bad_locale) + ) + + raise RuntimeError("\n\n".join(extra)) diff --git a/flask-app/venv/lib/python3.8/site-packages/click/_winconsole.py b/flask-app/venv/lib/python3.8/site-packages/click/_winconsole.py new file mode 100644 index 000000000..6b20df315 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click/_winconsole.py @@ -0,0 +1,279 @@ +# This module is based on the excellent work by Adam Bartoš who +# provided a lot of what went into the implementation here in +# the discussion to issue1602 in the Python bug tracker. +# +# There are some general differences in regards to how this works +# compared to the original patches as we do not need to patch +# the entire interpreter but just work in our little world of +# echo and prompt. +import io +import sys +import time +import typing as t +from ctypes import byref +from ctypes import c_char +from ctypes import c_char_p +from ctypes import c_int +from ctypes import c_ssize_t +from ctypes import c_ulong +from ctypes import c_void_p +from ctypes import POINTER +from ctypes import py_object +from ctypes import Structure +from ctypes.wintypes import DWORD +from ctypes.wintypes import HANDLE +from ctypes.wintypes import LPCWSTR +from ctypes.wintypes import LPWSTR + +from ._compat import _NonClosingTextIOWrapper + +assert sys.platform == "win32" +import msvcrt # noqa: E402 +from ctypes import windll # noqa: E402 +from ctypes import WINFUNCTYPE # noqa: E402 + +c_ssize_p = POINTER(c_ssize_t) + +kernel32 = windll.kernel32 +GetStdHandle = kernel32.GetStdHandle +ReadConsoleW = kernel32.ReadConsoleW +WriteConsoleW = kernel32.WriteConsoleW +GetConsoleMode = kernel32.GetConsoleMode +GetLastError = kernel32.GetLastError +GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommandLineW", windll.kernel32)) +CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))( + ("CommandLineToArgvW", windll.shell32) +) +LocalFree = WINFUNCTYPE(c_void_p, c_void_p)(("LocalFree", windll.kernel32)) + +STDIN_HANDLE = GetStdHandle(-10) +STDOUT_HANDLE = GetStdHandle(-11) +STDERR_HANDLE = GetStdHandle(-12) + +PyBUF_SIMPLE = 0 +PyBUF_WRITABLE = 1 + +ERROR_SUCCESS = 0 +ERROR_NOT_ENOUGH_MEMORY = 8 +ERROR_OPERATION_ABORTED = 995 + +STDIN_FILENO = 0 +STDOUT_FILENO = 1 +STDERR_FILENO = 2 + +EOF = b"\x1a" +MAX_BYTES_WRITTEN = 32767 + +try: + from ctypes import pythonapi +except ImportError: + # On PyPy we cannot get buffers so our ability to operate here is + # severely limited. + get_buffer = None +else: + + class Py_buffer(Structure): + _fields_ = [ + ("buf", c_void_p), + ("obj", py_object), + ("len", c_ssize_t), + ("itemsize", c_ssize_t), + ("readonly", c_int), + ("ndim", c_int), + ("format", c_char_p), + ("shape", c_ssize_p), + ("strides", c_ssize_p), + ("suboffsets", c_ssize_p), + ("internal", c_void_p), + ] + + PyObject_GetBuffer = pythonapi.PyObject_GetBuffer + PyBuffer_Release = pythonapi.PyBuffer_Release + + def get_buffer(obj, writable=False): + buf = Py_buffer() + flags = PyBUF_WRITABLE if writable else PyBUF_SIMPLE + PyObject_GetBuffer(py_object(obj), byref(buf), flags) + + try: + buffer_type = c_char * buf.len + return buffer_type.from_address(buf.buf) + finally: + PyBuffer_Release(byref(buf)) + + +class _WindowsConsoleRawIOBase(io.RawIOBase): + def __init__(self, handle): + self.handle = handle + + def isatty(self): + super().isatty() + return True + + +class _WindowsConsoleReader(_WindowsConsoleRawIOBase): + def readable(self): + return True + + def readinto(self, b): + bytes_to_be_read = len(b) + if not bytes_to_be_read: + return 0 + elif bytes_to_be_read % 2: + raise ValueError( + "cannot read odd number of bytes from UTF-16-LE encoded console" + ) + + buffer = get_buffer(b, writable=True) + code_units_to_be_read = bytes_to_be_read // 2 + code_units_read = c_ulong() + + rv = ReadConsoleW( + HANDLE(self.handle), + buffer, + code_units_to_be_read, + byref(code_units_read), + None, + ) + if GetLastError() == ERROR_OPERATION_ABORTED: + # wait for KeyboardInterrupt + time.sleep(0.1) + if not rv: + raise OSError(f"Windows error: {GetLastError()}") + + if buffer[0] == EOF: + return 0 + return 2 * code_units_read.value + + +class _WindowsConsoleWriter(_WindowsConsoleRawIOBase): + def writable(self): + return True + + @staticmethod + def _get_error_message(errno): + if errno == ERROR_SUCCESS: + return "ERROR_SUCCESS" + elif errno == ERROR_NOT_ENOUGH_MEMORY: + return "ERROR_NOT_ENOUGH_MEMORY" + return f"Windows error {errno}" + + def write(self, b): + bytes_to_be_written = len(b) + buf = get_buffer(b) + code_units_to_be_written = min(bytes_to_be_written, MAX_BYTES_WRITTEN) // 2 + code_units_written = c_ulong() + + WriteConsoleW( + HANDLE(self.handle), + buf, + code_units_to_be_written, + byref(code_units_written), + None, + ) + bytes_written = 2 * code_units_written.value + + if bytes_written == 0 and bytes_to_be_written > 0: + raise OSError(self._get_error_message(GetLastError())) + return bytes_written + + +class ConsoleStream: + def __init__(self, text_stream: t.TextIO, byte_stream: t.BinaryIO) -> None: + self._text_stream = text_stream + self.buffer = byte_stream + + @property + def name(self) -> str: + return self.buffer.name + + def write(self, x: t.AnyStr) -> int: + if isinstance(x, str): + return self._text_stream.write(x) + try: + self.flush() + except Exception: + pass + return self.buffer.write(x) + + def writelines(self, lines: t.Iterable[t.AnyStr]) -> None: + for line in lines: + self.write(line) + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._text_stream, name) + + def isatty(self) -> bool: + return self.buffer.isatty() + + def __repr__(self): + return f"" + + +def _get_text_stdin(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedReader(_WindowsConsoleReader(STDIN_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stdout(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDOUT_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +def _get_text_stderr(buffer_stream: t.BinaryIO) -> t.TextIO: + text_stream = _NonClosingTextIOWrapper( + io.BufferedWriter(_WindowsConsoleWriter(STDERR_HANDLE)), + "utf-16-le", + "strict", + line_buffering=True, + ) + return t.cast(t.TextIO, ConsoleStream(text_stream, buffer_stream)) + + +_stream_factories: t.Mapping[int, t.Callable[[t.BinaryIO], t.TextIO]] = { + 0: _get_text_stdin, + 1: _get_text_stdout, + 2: _get_text_stderr, +} + + +def _is_console(f: t.TextIO) -> bool: + if not hasattr(f, "fileno"): + return False + + try: + fileno = f.fileno() + except (OSError, io.UnsupportedOperation): + return False + + handle = msvcrt.get_osfhandle(fileno) + return bool(GetConsoleMode(handle, byref(DWORD()))) + + +def _get_windows_console_stream( + f: t.TextIO, encoding: t.Optional[str], errors: t.Optional[str] +) -> t.Optional[t.TextIO]: + if ( + get_buffer is not None + and encoding in {"utf-16-le", None} + and errors in {"strict", None} + and _is_console(f) + ): + func = _stream_factories.get(f.fileno()) + if func is not None: + b = getattr(f, "buffer", None) + + if b is None: + return None + + return func(b) diff --git a/flask-app/venv/lib/python3.8/site-packages/click/core.py b/flask-app/venv/lib/python3.8/site-packages/click/core.py new file mode 100644 index 000000000..f2263544a --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click/core.py @@ -0,0 +1,2953 @@ +import enum +import errno +import os +import sys +import typing +import typing as t +from collections import abc +from contextlib import contextmanager +from contextlib import ExitStack +from functools import partial +from functools import update_wrapper +from gettext import gettext as _ +from gettext import ngettext +from itertools import repeat + +from . import types +from ._unicodefun import _verify_python_env +from .exceptions import Abort +from .exceptions import BadParameter +from .exceptions import ClickException +from .exceptions import Exit +from .exceptions import MissingParameter +from .exceptions import UsageError +from .formatting import HelpFormatter +from .formatting import join_options +from .globals import pop_context +from .globals import push_context +from .parser import _flag_needs_value +from .parser import OptionParser +from .parser import split_opt +from .termui import confirm +from .termui import prompt +from .termui import style +from .utils import _detect_program_name +from .utils import _expand_args +from .utils import echo +from .utils import make_default_short_help +from .utils import make_str +from .utils import PacifyFlushWrapper + +if t.TYPE_CHECKING: + import typing_extensions as te + from .shell_completion import CompletionItem + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) +V = t.TypeVar("V") + + +def _complete_visible_commands( + ctx: "Context", incomplete: str +) -> t.Iterator[t.Tuple[str, "Command"]]: + """List all the subcommands of a group that start with the + incomplete value and aren't hidden. + + :param ctx: Invocation context for the group. + :param incomplete: Value being completed. May be empty. + """ + multi = t.cast(MultiCommand, ctx.command) + + for name in multi.list_commands(ctx): + if name.startswith(incomplete): + command = multi.get_command(ctx, name) + + if command is not None and not command.hidden: + yield name, command + + +def _check_multicommand( + base_command: "MultiCommand", cmd_name: str, cmd: "Command", register: bool = False +) -> None: + if not base_command.chain or not isinstance(cmd, MultiCommand): + return + if register: + hint = ( + "It is not possible to add multi commands as children to" + " another multi command that is in chain mode." + ) + else: + hint = ( + "Found a multi command as subcommand to a multi command" + " that is in chain mode. This is not supported." + ) + raise RuntimeError( + f"{hint}. Command {base_command.name!r} is set to chain and" + f" {cmd_name!r} was added as a subcommand but it in itself is a" + f" multi command. ({cmd_name!r} is a {type(cmd).__name__}" + f" within a chained {type(base_command).__name__} named" + f" {base_command.name!r})." + ) + + +def batch(iterable: t.Iterable[V], batch_size: int) -> t.List[t.Tuple[V, ...]]: + return list(zip(*repeat(iter(iterable), batch_size))) + + +@contextmanager +def augment_usage_errors( + ctx: "Context", param: t.Optional["Parameter"] = None +) -> t.Iterator[None]: + """Context manager that attaches extra information to exceptions.""" + try: + yield + except BadParameter as e: + if e.ctx is None: + e.ctx = ctx + if param is not None and e.param is None: + e.param = param + raise + except UsageError as e: + if e.ctx is None: + e.ctx = ctx + raise + + +def iter_params_for_processing( + invocation_order: t.Sequence["Parameter"], + declaration_order: t.Sequence["Parameter"], +) -> t.List["Parameter"]: + """Given a sequence of parameters in the order as should be considered + for processing and an iterable of parameters that exist, this returns + a list in the correct order as they should be processed. + """ + + def sort_key(item: "Parameter") -> t.Tuple[bool, float]: + try: + idx: float = invocation_order.index(item) + except ValueError: + idx = float("inf") + + return not item.is_eager, idx + + return sorted(declaration_order, key=sort_key) + + +class ParameterSource(enum.Enum): + """This is an :class:`~enum.Enum` that indicates the source of a + parameter's value. + + Use :meth:`click.Context.get_parameter_source` to get the + source for a parameter by name. + + .. versionchanged:: 8.0 + Use :class:`~enum.Enum` and drop the ``validate`` method. + + .. versionchanged:: 8.0 + Added the ``PROMPT`` value. + """ + + COMMANDLINE = enum.auto() + """The value was provided by the command line args.""" + ENVIRONMENT = enum.auto() + """The value was provided with an environment variable.""" + DEFAULT = enum.auto() + """Used the default specified by the parameter.""" + DEFAULT_MAP = enum.auto() + """Used a default provided by :attr:`Context.default_map`.""" + PROMPT = enum.auto() + """Used a prompt to confirm a default or provide a value.""" + + +class Context: + """The context is a special internal object that holds state relevant + for the script execution at every single level. It's normally invisible + to commands unless they opt-in to getting access to it. + + The context is useful as it can pass internal objects around and can + control special execution features such as reading data from + environment variables. + + A context can be used as context manager in which case it will call + :meth:`close` on teardown. + + :param command: the command class for this context. + :param parent: the parent context. + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it is usually + the name of the script, for commands below it it's + the name of the script. + :param obj: an arbitrary object of user data. + :param auto_envvar_prefix: the prefix to use for automatic environment + variables. If this is `None` then reading + from environment variables is disabled. This + does not affect manually set environment + variables which are always read. + :param default_map: a dictionary (like object) with default values + for parameters. + :param terminal_width: the width of the terminal. The default is + inherit from parent context. If no context + defines the terminal width then auto + detection will be applied. + :param max_content_width: the maximum width for content rendered by + Click (this currently only affects help + pages). This defaults to 80 characters if + not overridden. In other words: even if the + terminal is larger than that, Click will not + format things wider than 80 characters by + default. In addition to that, formatters might + add some safety mapping on the right. + :param resilient_parsing: if this flag is enabled then Click will + parse without any interactivity or callback + invocation. Default values will also be + ignored. This is useful for implementing + things such as completion support. + :param allow_extra_args: if this is set to `True` then extra arguments + at the end will not raise an error and will be + kept on the context. The default is to inherit + from the command. + :param allow_interspersed_args: if this is set to `False` then options + and arguments cannot be mixed. The + default is to inherit from the command. + :param ignore_unknown_options: instructs click to ignore options it does + not know and keeps them for later + processing. + :param help_option_names: optionally a list of strings that define how + the default help parameter is named. The + default is ``['--help']``. + :param token_normalize_func: an optional function that is used to + normalize tokens (options, choices, + etc.). This for instance can be used to + implement case insensitive behavior. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are used in texts that Click prints which is by + default not the case. This for instance would affect + help output. + :param show_default: Show defaults for all options. If not set, + defaults to the value from a parent context. Overrides an + option's ``show_default`` argument. + + .. versionchanged:: 8.0 + The ``show_default`` parameter defaults to the value from the + parent context. + + .. versionchanged:: 7.1 + Added the ``show_default`` parameter. + + .. versionchanged:: 4.0 + Added the ``color``, ``ignore_unknown_options``, and + ``max_content_width`` parameters. + + .. versionchanged:: 3.0 + Added the ``allow_extra_args`` and ``allow_interspersed_args`` + parameters. + + .. versionchanged:: 2.0 + Added the ``resilient_parsing``, ``help_option_names``, and + ``token_normalize_func`` parameters. + """ + + #: The formatter class to create with :meth:`make_formatter`. + #: + #: .. versionadded:: 8.0 + formatter_class: t.Type["HelpFormatter"] = HelpFormatter + + def __init__( + self, + command: "Command", + parent: t.Optional["Context"] = None, + info_name: t.Optional[str] = None, + obj: t.Optional[t.Any] = None, + auto_envvar_prefix: t.Optional[str] = None, + default_map: t.Optional[t.Dict[str, t.Any]] = None, + terminal_width: t.Optional[int] = None, + max_content_width: t.Optional[int] = None, + resilient_parsing: bool = False, + allow_extra_args: t.Optional[bool] = None, + allow_interspersed_args: t.Optional[bool] = None, + ignore_unknown_options: t.Optional[bool] = None, + help_option_names: t.Optional[t.List[str]] = None, + token_normalize_func: t.Optional[t.Callable[[str], str]] = None, + color: t.Optional[bool] = None, + show_default: t.Optional[bool] = None, + ) -> None: + #: the parent context or `None` if none exists. + self.parent = parent + #: the :class:`Command` for this context. + self.command = command + #: the descriptive information name + self.info_name = info_name + #: Map of parameter names to their parsed values. Parameters + #: with ``expose_value=False`` are not stored. + self.params: t.Dict[str, t.Any] = {} + #: the leftover arguments. + self.args: t.List[str] = [] + #: protected arguments. These are arguments that are prepended + #: to `args` when certain parsing scenarios are encountered but + #: must be never propagated to another arguments. This is used + #: to implement nested parsing. + self.protected_args: t.List[str] = [] + + if obj is None and parent is not None: + obj = parent.obj + + #: the user object stored. + self.obj: t.Any = obj + self._meta: t.Dict[str, t.Any] = getattr(parent, "meta", {}) + + #: A dictionary (-like object) with defaults for parameters. + if ( + default_map is None + and info_name is not None + and parent is not None + and parent.default_map is not None + ): + default_map = parent.default_map.get(info_name) + + self.default_map: t.Optional[t.Dict[str, t.Any]] = default_map + + #: This flag indicates if a subcommand is going to be executed. A + #: group callback can use this information to figure out if it's + #: being executed directly or because the execution flow passes + #: onwards to a subcommand. By default it's None, but it can be + #: the name of the subcommand to execute. + #: + #: If chaining is enabled this will be set to ``'*'`` in case + #: any commands are executed. It is however not possible to + #: figure out which ones. If you require this knowledge you + #: should use a :func:`result_callback`. + self.invoked_subcommand: t.Optional[str] = None + + if terminal_width is None and parent is not None: + terminal_width = parent.terminal_width + + #: The width of the terminal (None is autodetection). + self.terminal_width: t.Optional[int] = terminal_width + + if max_content_width is None and parent is not None: + max_content_width = parent.max_content_width + + #: The maximum width of formatted content (None implies a sensible + #: default which is 80 for most things). + self.max_content_width: t.Optional[int] = max_content_width + + if allow_extra_args is None: + allow_extra_args = command.allow_extra_args + + #: Indicates if the context allows extra args or if it should + #: fail on parsing. + #: + #: .. versionadded:: 3.0 + self.allow_extra_args = allow_extra_args + + if allow_interspersed_args is None: + allow_interspersed_args = command.allow_interspersed_args + + #: Indicates if the context allows mixing of arguments and + #: options or not. + #: + #: .. versionadded:: 3.0 + self.allow_interspersed_args: bool = allow_interspersed_args + + if ignore_unknown_options is None: + ignore_unknown_options = command.ignore_unknown_options + + #: Instructs click to ignore options that a command does not + #: understand and will store it on the context for later + #: processing. This is primarily useful for situations where you + #: want to call into external programs. Generally this pattern is + #: strongly discouraged because it's not possibly to losslessly + #: forward all arguments. + #: + #: .. versionadded:: 4.0 + self.ignore_unknown_options: bool = ignore_unknown_options + + if help_option_names is None: + if parent is not None: + help_option_names = parent.help_option_names + else: + help_option_names = ["--help"] + + #: The names for the help options. + self.help_option_names: t.List[str] = help_option_names + + if token_normalize_func is None and parent is not None: + token_normalize_func = parent.token_normalize_func + + #: An optional normalization function for tokens. This is + #: options, choices, commands etc. + self.token_normalize_func: t.Optional[ + t.Callable[[str], str] + ] = token_normalize_func + + #: Indicates if resilient parsing is enabled. In that case Click + #: will do its best to not cause any failures and default values + #: will be ignored. Useful for completion. + self.resilient_parsing: bool = resilient_parsing + + # If there is no envvar prefix yet, but the parent has one and + # the command on this level has a name, we can expand the envvar + # prefix automatically. + if auto_envvar_prefix is None: + if ( + parent is not None + and parent.auto_envvar_prefix is not None + and self.info_name is not None + ): + auto_envvar_prefix = ( + f"{parent.auto_envvar_prefix}_{self.info_name.upper()}" + ) + else: + auto_envvar_prefix = auto_envvar_prefix.upper() + + if auto_envvar_prefix is not None: + auto_envvar_prefix = auto_envvar_prefix.replace("-", "_") + + self.auto_envvar_prefix: t.Optional[str] = auto_envvar_prefix + + if color is None and parent is not None: + color = parent.color + + #: Controls if styling output is wanted or not. + self.color: t.Optional[bool] = color + + if show_default is None and parent is not None: + show_default = parent.show_default + + #: Show option default values when formatting help text. + self.show_default: t.Optional[bool] = show_default + + self._close_callbacks: t.List[t.Callable[[], t.Any]] = [] + self._depth = 0 + self._parameter_source: t.Dict[str, ParameterSource] = {} + self._exit_stack = ExitStack() + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. This traverses the entire CLI + structure. + + .. code-block:: python + + with Context(cli) as ctx: + info = ctx.to_info_dict() + + .. versionadded:: 8.0 + """ + return { + "command": self.command.to_info_dict(self), + "info_name": self.info_name, + "allow_extra_args": self.allow_extra_args, + "allow_interspersed_args": self.allow_interspersed_args, + "ignore_unknown_options": self.ignore_unknown_options, + "auto_envvar_prefix": self.auto_envvar_prefix, + } + + def __enter__(self) -> "Context": + self._depth += 1 + push_context(self) + return self + + def __exit__(self, exc_type, exc_value, tb): # type: ignore + self._depth -= 1 + if self._depth == 0: + self.close() + pop_context() + + @contextmanager + def scope(self, cleanup: bool = True) -> t.Iterator["Context"]: + """This helper method can be used with the context object to promote + it to the current thread local (see :func:`get_current_context`). + The default behavior of this is to invoke the cleanup functions which + can be disabled by setting `cleanup` to `False`. The cleanup + functions are typically used for things such as closing file handles. + + If the cleanup is intended the context object can also be directly + used as a context manager. + + Example usage:: + + with ctx.scope(): + assert get_current_context() is ctx + + This is equivalent:: + + with ctx: + assert get_current_context() is ctx + + .. versionadded:: 5.0 + + :param cleanup: controls if the cleanup functions should be run or + not. The default is to run these functions. In + some situations the context only wants to be + temporarily pushed in which case this can be disabled. + Nested pushes automatically defer the cleanup. + """ + if not cleanup: + self._depth += 1 + try: + with self as rv: + yield rv + finally: + if not cleanup: + self._depth -= 1 + + @property + def meta(self) -> t.Dict[str, t.Any]: + """This is a dictionary which is shared with all the contexts + that are nested. It exists so that click utilities can store some + state here if they need to. It is however the responsibility of + that code to manage this dictionary well. + + The keys are supposed to be unique dotted strings. For instance + module paths are a good choice for it. What is stored in there is + irrelevant for the operation of click. However what is important is + that code that places data here adheres to the general semantics of + the system. + + Example usage:: + + LANG_KEY = f'{__name__}.lang' + + def set_language(value): + ctx = get_current_context() + ctx.meta[LANG_KEY] = value + + def get_language(): + return get_current_context().meta.get(LANG_KEY, 'en_US') + + .. versionadded:: 5.0 + """ + return self._meta + + def make_formatter(self) -> HelpFormatter: + """Creates the :class:`~click.HelpFormatter` for the help and + usage output. + + To quickly customize the formatter class used without overriding + this method, set the :attr:`formatter_class` attribute. + + .. versionchanged:: 8.0 + Added the :attr:`formatter_class` attribute. + """ + return self.formatter_class( + width=self.terminal_width, max_width=self.max_content_width + ) + + def with_resource(self, context_manager: t.ContextManager[V]) -> V: + """Register a resource as if it were used in a ``with`` + statement. The resource will be cleaned up when the context is + popped. + + Uses :meth:`contextlib.ExitStack.enter_context`. It calls the + resource's ``__enter__()`` method and returns the result. When + the context is popped, it closes the stack, which calls the + resource's ``__exit__()`` method. + + To register a cleanup function for something that isn't a + context manager, use :meth:`call_on_close`. Or use something + from :mod:`contextlib` to turn it into a context manager first. + + .. code-block:: python + + @click.group() + @click.option("--name") + @click.pass_context + def cli(ctx): + ctx.obj = ctx.with_resource(connect_db(name)) + + :param context_manager: The context manager to enter. + :return: Whatever ``context_manager.__enter__()`` returns. + + .. versionadded:: 8.0 + """ + return self._exit_stack.enter_context(context_manager) + + def call_on_close(self, f: t.Callable[..., t.Any]) -> t.Callable[..., t.Any]: + """Register a function to be called when the context tears down. + + This can be used to close resources opened during the script + execution. Resources that support Python's context manager + protocol which would be used in a ``with`` statement should be + registered with :meth:`with_resource` instead. + + :param f: The function to execute on teardown. + """ + return self._exit_stack.callback(f) + + def close(self) -> None: + """Invoke all close callbacks registered with + :meth:`call_on_close`, and exit all context managers entered + with :meth:`with_resource`. + """ + self._exit_stack.close() + # In case the context is reused, create a new exit stack. + self._exit_stack = ExitStack() + + @property + def command_path(self) -> str: + """The computed command path. This is used for the ``usage`` + information on the help page. It's automatically created by + combining the info names of the chain of contexts to the root. + """ + rv = "" + if self.info_name is not None: + rv = self.info_name + if self.parent is not None: + parent_command_path = [self.parent.command_path] + + if isinstance(self.parent.command, Command): + for param in self.parent.command.get_params(self): + parent_command_path.extend(param.get_usage_pieces(self)) + + rv = f"{' '.join(parent_command_path)} {rv}" + return rv.lstrip() + + def find_root(self) -> "Context": + """Finds the outermost context.""" + node = self + while node.parent is not None: + node = node.parent + return node + + def find_object(self, object_type: t.Type[V]) -> t.Optional[V]: + """Finds the closest object of a given type.""" + node: t.Optional["Context"] = self + + while node is not None: + if isinstance(node.obj, object_type): + return node.obj + + node = node.parent + + return None + + def ensure_object(self, object_type: t.Type[V]) -> V: + """Like :meth:`find_object` but sets the innermost object to a + new instance of `object_type` if it does not exist. + """ + rv = self.find_object(object_type) + if rv is None: + self.obj = rv = object_type() + return rv + + @typing.overload + def lookup_default( + self, name: str, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @typing.overload + def lookup_default( + self, name: str, call: "te.Literal[False]" = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + ... + + def lookup_default(self, name: str, call: bool = True) -> t.Optional[t.Any]: + """Get the default for a parameter from :attr:`default_map`. + + :param name: Name of the parameter. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + if self.default_map is not None: + value = self.default_map.get(name) + + if call and callable(value): + return value() + + return value + + return None + + def fail(self, message: str) -> "te.NoReturn": + """Aborts the execution of the program with a specific error + message. + + :param message: the error message to fail with. + """ + raise UsageError(message, self) + + def abort(self) -> "te.NoReturn": + """Aborts the script.""" + raise Abort() + + def exit(self, code: int = 0) -> "te.NoReturn": + """Exits the application with a given exit code.""" + raise Exit(code) + + def get_usage(self) -> str: + """Helper method to get formatted usage string for the current + context and command. + """ + return self.command.get_usage(self) + + def get_help(self) -> str: + """Helper method to get formatted help page for the current + context and command. + """ + return self.command.get_help(self) + + def _make_sub_context(self, command: "Command") -> "Context": + """Create a new context of the same type as this context, but + for a new command. + + :meta private: + """ + return type(self)(command, info_name=command.name, parent=self) + + def invoke( + __self, # noqa: B902 + __callback: t.Union["Command", t.Callable[..., t.Any]], + *args: t.Any, + **kwargs: t.Any, + ) -> t.Any: + """Invokes a command callback in exactly the way it expects. There + are two ways to invoke this method: + + 1. the first argument can be a callback and all other arguments and + keyword arguments are forwarded directly to the function. + 2. the first argument is a click command object. In that case all + arguments are forwarded as well but proper click parameters + (options and click arguments) must be keyword arguments and Click + will fill in defaults. + + Note that before Click 3.2 keyword arguments were not properly filled + in against the intention of this code and no context was created. For + more information about this change and why it was done in a bugfix + release see :ref:`upgrade-to-3.2`. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if :meth:`forward` is called at multiple levels. + """ + if isinstance(__callback, Command): + other_cmd = __callback + + if other_cmd.callback is None: + raise TypeError( + "The given command does not have a callback that can be invoked." + ) + else: + __callback = other_cmd.callback + + ctx = __self._make_sub_context(other_cmd) + + for param in other_cmd.params: + if param.name not in kwargs and param.expose_value: + kwargs[param.name] = param.type_cast_value( # type: ignore + ctx, param.get_default(ctx) + ) + + # Track all kwargs as params, so that forward() will pass + # them on in subsequent calls. + ctx.params.update(kwargs) + else: + ctx = __self + + with augment_usage_errors(__self): + with ctx: + return __callback(*args, **kwargs) + + def forward( + __self, __cmd: "Command", *args: t.Any, **kwargs: t.Any # noqa: B902 + ) -> t.Any: + """Similar to :meth:`invoke` but fills in default keyword + arguments from the current context if the other command expects + it. This cannot invoke callbacks directly, only other commands. + + .. versionchanged:: 8.0 + All ``kwargs`` are tracked in :attr:`params` so they will be + passed if ``forward`` is called at multiple levels. + """ + # Can only forward to other commands, not direct callbacks. + if not isinstance(__cmd, Command): + raise TypeError("Callback is not a command.") + + for param in __self.params: + if param not in kwargs: + kwargs[param] = __self.params[param] + + return __self.invoke(__cmd, *args, **kwargs) + + def set_parameter_source(self, name: str, source: ParameterSource) -> None: + """Set the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + :param name: The name of the parameter. + :param source: A member of :class:`~click.core.ParameterSource`. + """ + self._parameter_source[name] = source + + def get_parameter_source(self, name: str) -> t.Optional[ParameterSource]: + """Get the source of a parameter. This indicates the location + from which the value of the parameter was obtained. + + This can be useful for determining when a user specified a value + on the command line that is the same as the default value. It + will be :attr:`~click.core.ParameterSource.DEFAULT` only if the + value was actually taken from the default. + + :param name: The name of the parameter. + :rtype: ParameterSource + + .. versionchanged:: 8.0 + Returns ``None`` if the parameter was not provided from any + source. + """ + return self._parameter_source.get(name) + + +class BaseCommand: + """The base command implements the minimal API contract of commands. + Most code will never use this as it does not implement a lot of useful + functionality but it can act as the direct subclass of alternative + parsing methods that do not depend on the Click parser. + + For instance, this can be used to bridge Click and other systems like + argparse or docopt. + + Because base commands do not implement a lot of the API that other + parts of Click take for granted, they are not supported for all + operations. For instance, they cannot be used with the decorators + usually and they have no built-in callback system. + + .. versionchanged:: 2.0 + Added the `context_settings` parameter. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + """ + + #: The context class to create with :meth:`make_context`. + #: + #: .. versionadded:: 8.0 + context_class: t.Type[Context] = Context + #: the default for the :attr:`Context.allow_extra_args` flag. + allow_extra_args = False + #: the default for the :attr:`Context.allow_interspersed_args` flag. + allow_interspersed_args = True + #: the default for the :attr:`Context.ignore_unknown_options` flag. + ignore_unknown_options = False + + def __init__( + self, + name: t.Optional[str], + context_settings: t.Optional[t.Dict[str, t.Any]] = None, + ) -> None: + #: the name the command thinks it has. Upon registering a command + #: on a :class:`Group` the group will default the command name + #: with this information. You should instead use the + #: :class:`Context`\'s :attr:`~Context.info_name` attribute. + self.name = name + + if context_settings is None: + context_settings = {} + + #: an optional dictionary with defaults passed to the context. + self.context_settings: t.Dict[str, t.Any] = context_settings + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. This traverses the entire structure + below this command. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + :param ctx: A :class:`Context` representing this command. + + .. versionadded:: 8.0 + """ + return {"name": self.name} + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def get_usage(self, ctx: Context) -> str: + raise NotImplementedError("Base commands cannot get usage") + + def get_help(self, ctx: Context) -> str: + raise NotImplementedError("Base commands cannot get help") + + def make_context( + self, + info_name: t.Optional[str], + args: t.List[str], + parent: t.Optional[Context] = None, + **extra: t.Any, + ) -> Context: + """This function when given an info name and arguments will kick + off the parsing and create a new :class:`Context`. It does not + invoke the actual command callback though. + + To quickly customize the context class used without overriding + this method, set the :attr:`context_class` attribute. + + :param info_name: the info name for this invocation. Generally this + is the most descriptive name for the script or + command. For the toplevel script it's usually + the name of the script, for commands below it it's + the name of the command. + :param args: the arguments to parse as list of strings. + :param parent: the parent context if available. + :param extra: extra keyword arguments forwarded to the context + constructor. + + .. versionchanged:: 8.0 + Added the :attr:`context_class` attribute. + """ + for key, value in self.context_settings.items(): + if key not in extra: + extra[key] = value + + ctx = self.context_class( + self, info_name=info_name, parent=parent, **extra # type: ignore + ) + + with ctx.scope(cleanup=False): + self.parse_args(ctx, args) + return ctx + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + """Given a context and a list of arguments this creates the parser + and parses the arguments, then modifies the context as necessary. + This is automatically invoked by :meth:`make_context`. + """ + raise NotImplementedError("Base commands do not know how to parse arguments.") + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the command. The default + implementation is raising a not implemented error. + """ + raise NotImplementedError("Base commands are not invokable by default") + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of chained multi-commands. + + Any command could be part of a chained multi-command, so sibling + commands are valid at any point during command completion. Other + command classes will return more completions. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results: t.List["CompletionItem"] = [] + + while ctx.parent is not None: + ctx = ctx.parent + + if isinstance(ctx.command, MultiCommand) and ctx.command.chain: + results.extend( + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + if name not in ctx.protected_args + ) + + return results + + @typing.overload + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: "te.Literal[True]" = True, + **extra: t.Any, + ) -> "te.NoReturn": + ... + + @typing.overload + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: bool = ..., + **extra: t.Any, + ) -> t.Any: + ... + + def main( + self, + args: t.Optional[t.Sequence[str]] = None, + prog_name: t.Optional[str] = None, + complete_var: t.Optional[str] = None, + standalone_mode: bool = True, + windows_expand_args: bool = True, + **extra: t.Any, + ) -> t.Any: + """This is the way to invoke a script with all the bells and + whistles as a command line application. This will always terminate + the application after a call. If this is not wanted, ``SystemExit`` + needs to be caught. + + This method is also available by directly calling the instance of + a :class:`Command`. + + :param args: the arguments that should be used for parsing. If not + provided, ``sys.argv[1:]`` is used. + :param prog_name: the program name that should be used. By default + the program name is constructed by taking the file + name from ``sys.argv[0]``. + :param complete_var: the environment variable that controls the + bash completion support. The default is + ``"__COMPLETE"`` with prog_name in + uppercase. + :param standalone_mode: the default behavior is to invoke the script + in standalone mode. Click will then + handle exceptions and convert them into + error messages and the function will never + return but shut down the interpreter. If + this is set to `False` they will be + propagated to the caller and the return + value of this function is the return value + of :meth:`invoke`. + :param windows_expand_args: Expand glob patterns, user dir, and + env vars in command line args on Windows. + :param extra: extra keyword arguments are forwarded to the context + constructor. See :class:`Context` for more information. + + .. versionchanged:: 8.0.1 + Added the ``windows_expand_args`` parameter to allow + disabling command line arg expansion on Windows. + + .. versionchanged:: 8.0 + When taking arguments from ``sys.argv`` on Windows, glob + patterns, user dir, and env vars are expanded. + + .. versionchanged:: 3.0 + Added the ``standalone_mode`` parameter. + """ + # Verify that the environment is configured correctly, or reject + # further execution to avoid a broken script. + _verify_python_env() + + if args is None: + args = sys.argv[1:] + + if os.name == "nt" and windows_expand_args: + args = _expand_args(args) + else: + args = list(args) + + if prog_name is None: + prog_name = _detect_program_name() + + # Process shell completion requests and exit early. + self._main_shell_completion(extra, prog_name, complete_var) + + try: + try: + with self.make_context(prog_name, args, **extra) as ctx: + rv = self.invoke(ctx) + if not standalone_mode: + return rv + # it's not safe to `ctx.exit(rv)` here! + # note that `rv` may actually contain data like "1" which + # has obvious effects + # more subtle case: `rv=[None, None]` can come out of + # chained commands which all returned `None` -- so it's not + # even always obvious that `rv` indicates success/failure + # by its truthiness/falsiness + ctx.exit() + except (EOFError, KeyboardInterrupt): + echo(file=sys.stderr) + raise Abort() from None + except ClickException as e: + if not standalone_mode: + raise + e.show() + sys.exit(e.exit_code) + except OSError as e: + if e.errno == errno.EPIPE: + sys.stdout = t.cast(t.TextIO, PacifyFlushWrapper(sys.stdout)) + sys.stderr = t.cast(t.TextIO, PacifyFlushWrapper(sys.stderr)) + sys.exit(1) + else: + raise + except Exit as e: + if standalone_mode: + sys.exit(e.exit_code) + else: + # in non-standalone mode, return the exit code + # note that this is only reached if `self.invoke` above raises + # an Exit explicitly -- thus bypassing the check there which + # would return its result + # the results of non-standalone execution may therefore be + # somewhat ambiguous: if there are codepaths which lead to + # `ctx.exit(1)` and to `return 1`, the caller won't be able to + # tell the difference between the two + return e.exit_code + except Abort: + if not standalone_mode: + raise + echo(_("Aborted!"), file=sys.stderr) + sys.exit(1) + + def _main_shell_completion( + self, + ctx_args: t.Dict[str, t.Any], + prog_name: str, + complete_var: t.Optional[str] = None, + ) -> None: + """Check if the shell is asking for tab completion, process + that, then exit early. Called from :meth:`main` before the + program is invoked. + + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. Defaults to + ``_{PROG_NAME}_COMPLETE``. + """ + if complete_var is None: + complete_var = f"_{prog_name}_COMPLETE".replace("-", "_").upper() + + instruction = os.environ.get(complete_var) + + if not instruction: + return + + from .shell_completion import shell_complete + + rv = shell_complete(self, ctx_args, prog_name, complete_var, instruction) + sys.exit(rv) + + def __call__(self, *args: t.Any, **kwargs: t.Any) -> t.Any: + """Alias for :meth:`main`.""" + return self.main(*args, **kwargs) + + +class Command(BaseCommand): + """Commands are the basic building block of command line interfaces in + Click. A basic command handles command line parsing and might dispatch + more parsing to commands nested below it. + + .. versionchanged:: 2.0 + Added the `context_settings` parameter. + .. versionchanged:: 8.0 + Added repr showing the command name + .. versionchanged:: 7.1 + Added the `no_args_is_help` parameter. + + :param name: the name of the command to use unless a group overrides it. + :param context_settings: an optional dictionary with defaults that are + passed to the context object. + :param callback: the callback to invoke. This is optional. + :param params: the parameters to register with this command. This can + be either :class:`Option` or :class:`Argument` objects. + :param help: the help string to use for this command. + :param epilog: like the help string but it's printed at the end of the + help page after everything else. + :param short_help: the short help to use for this command. This is + shown on the command listing of the parent command. + :param add_help_option: by default each command registers a ``--help`` + option. This can be disabled by this parameter. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is disabled by default. + If enabled this will add ``--help`` as argument + if no arguments are passed + :param hidden: hide this command from help outputs. + + :param deprecated: issues a message indicating that + the command is deprecated. + """ + + def __init__( + self, + name: t.Optional[str], + context_settings: t.Optional[t.Dict[str, t.Any]] = None, + callback: t.Optional[t.Callable[..., t.Any]] = None, + params: t.Optional[t.List["Parameter"]] = None, + help: t.Optional[str] = None, + epilog: t.Optional[str] = None, + short_help: t.Optional[str] = None, + options_metavar: t.Optional[str] = "[OPTIONS]", + add_help_option: bool = True, + no_args_is_help: bool = False, + hidden: bool = False, + deprecated: bool = False, + ) -> None: + super().__init__(name, context_settings) + #: the callback to execute when the command fires. This might be + #: `None` in which case nothing happens. + self.callback = callback + #: the list of parameters for this command in the order they + #: should show up in the help page and execute. Eager parameters + #: will automatically be handled before non eager ones. + self.params: t.List["Parameter"] = params or [] + + # if a form feed (page break) is found in the help text, truncate help + # text to the content preceding the first form feed + if help and "\f" in help: + help = help.split("\f", 1)[0] + + self.help = help + self.epilog = epilog + self.options_metavar = options_metavar + self.short_help = short_help + self.add_help_option = add_help_option + self.no_args_is_help = no_args_is_help + self.hidden = hidden + self.deprecated = deprecated + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + info_dict.update( + params=[param.to_info_dict() for param in self.get_params(ctx)], + help=self.help, + epilog=self.epilog, + short_help=self.short_help, + hidden=self.hidden, + deprecated=self.deprecated, + ) + return info_dict + + def get_usage(self, ctx: Context) -> str: + """Formats the usage line into a string and returns it. + + Calls :meth:`format_usage` internally. + """ + formatter = ctx.make_formatter() + self.format_usage(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_params(self, ctx: Context) -> t.List["Parameter"]: + rv = self.params + help_option = self.get_help_option(ctx) + + if help_option is not None: + rv = [*rv, help_option] + + return rv + + def format_usage(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the usage line into the formatter. + + This is a low-level method called by :meth:`get_usage`. + """ + pieces = self.collect_usage_pieces(ctx) + formatter.write_usage(ctx.command_path, " ".join(pieces)) + + def collect_usage_pieces(self, ctx: Context) -> t.List[str]: + """Returns all the pieces that go into the usage line and returns + it as a list of strings. + """ + rv = [self.options_metavar] if self.options_metavar else [] + + for param in self.get_params(ctx): + rv.extend(param.get_usage_pieces(ctx)) + + return rv + + def get_help_option_names(self, ctx: Context) -> t.List[str]: + """Returns the names for the help option.""" + all_names = set(ctx.help_option_names) + for param in self.params: + all_names.difference_update(param.opts) + all_names.difference_update(param.secondary_opts) + return list(all_names) + + def get_help_option(self, ctx: Context) -> t.Optional["Option"]: + """Returns the help option object.""" + help_options = self.get_help_option_names(ctx) + + if not help_options or not self.add_help_option: + return None + + def show_help(ctx: Context, param: "Parameter", value: str) -> None: + if value and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + return Option( + help_options, + is_flag=True, + is_eager=True, + expose_value=False, + callback=show_help, + help=_("Show this message and exit."), + ) + + def make_parser(self, ctx: Context) -> OptionParser: + """Creates the underlying option parser for this command.""" + parser = OptionParser(ctx) + for param in self.get_params(ctx): + param.add_to_parser(parser, ctx) + return parser + + def get_help(self, ctx: Context) -> str: + """Formats the help into a string and returns it. + + Calls :meth:`format_help` internally. + """ + formatter = ctx.make_formatter() + self.format_help(ctx, formatter) + return formatter.getvalue().rstrip("\n") + + def get_short_help_str(self, limit: int = 45) -> str: + """Gets short help for the command or makes it by shortening the + long help string. + """ + text = self.short_help or "" + + if not text and self.help: + text = make_default_short_help(self.help, limit) + + if self.deprecated: + text = _("(Deprecated) {text}").format(text=text) + + return text.strip() + + def format_help(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help into the formatter if it exists. + + This is a low-level method called by :meth:`get_help`. + + This calls the following methods: + + - :meth:`format_usage` + - :meth:`format_help_text` + - :meth:`format_options` + - :meth:`format_epilog` + """ + self.format_usage(ctx, formatter) + self.format_help_text(ctx, formatter) + self.format_options(ctx, formatter) + self.format_epilog(ctx, formatter) + + def format_help_text(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the help text to the formatter if it exists.""" + text = self.help or "" + + if self.deprecated: + text = _("(Deprecated) {text}").format(text=text) + + if text: + formatter.write_paragraph() + + with formatter.indentation(): + formatter.write_text(text) + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes all the options into the formatter if they exist.""" + opts = [] + for param in self.get_params(ctx): + rv = param.get_help_record(ctx) + if rv is not None: + opts.append(rv) + + if opts: + with formatter.section(_("Options")): + formatter.write_dl(opts) + + def format_epilog(self, ctx: Context, formatter: HelpFormatter) -> None: + """Writes the epilog into the formatter if it exists.""" + if self.epilog: + formatter.write_paragraph() + with formatter.indentation(): + formatter.write_text(self.epilog) + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + parser = self.make_parser(ctx) + opts, args, param_order = parser.parse_args(args=args) + + for param in iter_params_for_processing(param_order, self.get_params(ctx)): + value, args = param.handle_parse_result(ctx, opts, args) + + if args and not ctx.allow_extra_args and not ctx.resilient_parsing: + ctx.fail( + ngettext( + "Got unexpected extra argument ({args})", + "Got unexpected extra arguments ({args})", + len(args), + ).format(args=" ".join(map(str, args))) + ) + + ctx.args = args + return args + + def invoke(self, ctx: Context) -> t.Any: + """Given a context, this invokes the attached callback (if it exists) + in the right way. + """ + if self.deprecated: + message = _( + "DeprecationWarning: The command {name!r} is deprecated." + ).format(name=self.name) + echo(style(message, fg="red"), err=True) + + if self.callback is not None: + return ctx.invoke(self.callback, **ctx.params) + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of options and chained multi-commands. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results: t.List["CompletionItem"] = [] + + if incomplete and not incomplete[0].isalnum(): + for param in self.get_params(ctx): + if ( + not isinstance(param, Option) + or param.hidden + or ( + not param.multiple + and ctx.get_parameter_source(param.name) # type: ignore + is ParameterSource.COMMANDLINE + ) + ): + continue + + results.extend( + CompletionItem(name, help=param.help) + for name in [*param.opts, *param.secondary_opts] + if name.startswith(incomplete) + ) + + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class MultiCommand(Command): + """A multi command is the basic implementation of a command that + dispatches to subcommands. The most common version is the + :class:`Group`. + + :param invoke_without_command: this controls how the multi command itself + is invoked. By default it's only invoked + if a subcommand is provided. + :param no_args_is_help: this controls what happens if no arguments are + provided. This option is enabled by default if + `invoke_without_command` is disabled or disabled + if it's enabled. If enabled this will add + ``--help`` as argument if no arguments are + passed. + :param subcommand_metavar: the string that is used in the documentation + to indicate the subcommand place. + :param chain: if this is set to `True` chaining of multiple subcommands + is enabled. This restricts the form of commands in that + they cannot have optional arguments but it allows + multiple commands to be chained together. + :param result_callback: The result callback to attach to this multi + command. This can be set or changed later with the + :meth:`result_callback` decorator. + """ + + allow_extra_args = True + allow_interspersed_args = False + + def __init__( + self, + name: t.Optional[str] = None, + invoke_without_command: bool = False, + no_args_is_help: t.Optional[bool] = None, + subcommand_metavar: t.Optional[str] = None, + chain: bool = False, + result_callback: t.Optional[t.Callable[..., t.Any]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + + if no_args_is_help is None: + no_args_is_help = not invoke_without_command + + self.no_args_is_help = no_args_is_help + self.invoke_without_command = invoke_without_command + + if subcommand_metavar is None: + if chain: + subcommand_metavar = "COMMAND1 [ARGS]... [COMMAND2 [ARGS]...]..." + else: + subcommand_metavar = "COMMAND [ARGS]..." + + self.subcommand_metavar = subcommand_metavar + self.chain = chain + # The result callback that is stored. This can be set or + # overridden with the :func:`result_callback` decorator. + self._result_callback = result_callback + + if self.chain: + for param in self.params: + if isinstance(param, Argument) and not param.required: + raise RuntimeError( + "Multi commands in chain mode cannot have" + " optional arguments." + ) + + def to_info_dict(self, ctx: Context) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict(ctx) + commands = {} + + for name in self.list_commands(ctx): + command = self.get_command(ctx, name) + + if command is None: + continue + + sub_ctx = ctx._make_sub_context(command) + + with sub_ctx.scope(cleanup=False): + commands[name] = command.to_info_dict(sub_ctx) + + info_dict.update(commands=commands, chain=self.chain) + return info_dict + + def collect_usage_pieces(self, ctx: Context) -> t.List[str]: + rv = super().collect_usage_pieces(ctx) + rv.append(self.subcommand_metavar) + return rv + + def format_options(self, ctx: Context, formatter: HelpFormatter) -> None: + super().format_options(ctx, formatter) + self.format_commands(ctx, formatter) + + def result_callback(self, replace: bool = False) -> t.Callable[[F], F]: + """Adds a result callback to the command. By default if a + result callback is already registered this will chain them but + this can be disabled with the `replace` parameter. The result + callback is invoked with the return value of the subcommand + (or the list of return values from all subcommands if chaining + is enabled) as well as the parameters as they would be passed + to the main callback. + + Example:: + + @click.group() + @click.option('-i', '--input', default=23) + def cli(input): + return 42 + + @cli.result_callback() + def process_result(result, input): + return result + input + + :param replace: if set to `True` an already existing result + callback will be removed. + + .. versionchanged:: 8.0 + Renamed from ``resultcallback``. + + .. versionadded:: 3.0 + """ + + def decorator(f: F) -> F: + old_callback = self._result_callback + + if old_callback is None or replace: + self._result_callback = f + return f + + def function(__value, *args, **kwargs): # type: ignore + inner = old_callback(__value, *args, **kwargs) # type: ignore + return f(inner, *args, **kwargs) + + self._result_callback = rv = update_wrapper(t.cast(F, function), f) + return rv + + return decorator + + def resultcallback(self, replace: bool = False) -> t.Callable[[F], F]: + import warnings + + warnings.warn( + "'resultcallback' has been renamed to 'result_callback'." + " The old name will be removed in Click 8.1.", + DeprecationWarning, + stacklevel=2, + ) + return self.result_callback(replace=replace) + + def format_commands(self, ctx: Context, formatter: HelpFormatter) -> None: + """Extra format methods for multi methods that adds all the commands + after the options. + """ + commands = [] + for subcommand in self.list_commands(ctx): + cmd = self.get_command(ctx, subcommand) + # What is this, the tool lied about a command. Ignore it + if cmd is None: + continue + if cmd.hidden: + continue + + commands.append((subcommand, cmd)) + + # allow for 3 times the default spacing + if len(commands): + limit = formatter.width - 6 - max(len(cmd[0]) for cmd in commands) + + rows = [] + for subcommand, cmd in commands: + help = cmd.get_short_help_str(limit) + rows.append((subcommand, help)) + + if rows: + with formatter.section(_("Commands")): + formatter.write_dl(rows) + + def parse_args(self, ctx: Context, args: t.List[str]) -> t.List[str]: + if not args and self.no_args_is_help and not ctx.resilient_parsing: + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + rest = super().parse_args(ctx, args) + + if self.chain: + ctx.protected_args = rest + ctx.args = [] + elif rest: + ctx.protected_args, ctx.args = rest[:1], rest[1:] + + return ctx.args + + def invoke(self, ctx: Context) -> t.Any: + def _process_result(value: t.Any) -> t.Any: + if self._result_callback is not None: + value = ctx.invoke(self._result_callback, value, **ctx.params) + return value + + if not ctx.protected_args: + if self.invoke_without_command: + # No subcommand was invoked, so the result callback is + # invoked with None for regular groups, or an empty list + # for chained groups. + with ctx: + super().invoke(ctx) + return _process_result([] if self.chain else None) + ctx.fail(_("Missing command.")) + + # Fetch args back out + args = [*ctx.protected_args, *ctx.args] + ctx.args = [] + ctx.protected_args = [] + + # If we're not in chain mode, we only allow the invocation of a + # single command but we also inform the current context about the + # name of the command to invoke. + if not self.chain: + # Make sure the context is entered so we do not clean up + # resources until the result processor has worked. + with ctx: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + ctx.invoked_subcommand = cmd_name + super().invoke(ctx) + sub_ctx = cmd.make_context(cmd_name, args, parent=ctx) + with sub_ctx: + return _process_result(sub_ctx.command.invoke(sub_ctx)) + + # In chain mode we create the contexts step by step, but after the + # base command has been invoked. Because at that point we do not + # know the subcommands yet, the invoked subcommand attribute is + # set to ``*`` to inform the command that subcommands are executed + # but nothing else. + with ctx: + ctx.invoked_subcommand = "*" if args else None + super().invoke(ctx) + + # Otherwise we make every single context and invoke them in a + # chain. In that case the return value to the result processor + # is the list of all invoked subcommand's results. + contexts = [] + while args: + cmd_name, cmd, args = self.resolve_command(ctx, args) + assert cmd is not None + sub_ctx = cmd.make_context( + cmd_name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + ) + contexts.append(sub_ctx) + args, sub_ctx.args = sub_ctx.args, [] + + rv = [] + for sub_ctx in contexts: + with sub_ctx: + rv.append(sub_ctx.command.invoke(sub_ctx)) + return _process_result(rv) + + def resolve_command( + self, ctx: Context, args: t.List[str] + ) -> t.Tuple[t.Optional[str], t.Optional[Command], t.List[str]]: + cmd_name = make_str(args[0]) + original_cmd_name = cmd_name + + # Get the command + cmd = self.get_command(ctx, cmd_name) + + # If we can't find the command but there is a normalization + # function available, we try with that one. + if cmd is None and ctx.token_normalize_func is not None: + cmd_name = ctx.token_normalize_func(cmd_name) + cmd = self.get_command(ctx, cmd_name) + + # If we don't find the command we want to show an error message + # to the user that it was not provided. However, there is + # something else we should do: if the first argument looks like + # an option we want to kick off parsing again for arguments to + # resolve things like --help which now should go to the main + # place. + if cmd is None and not ctx.resilient_parsing: + if split_opt(cmd_name)[0]: + self.parse_args(ctx, ctx.args) + ctx.fail(_("No such command {name!r}.").format(name=original_cmd_name)) + return cmd_name if cmd else None, cmd, args[1:] + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + """Given a context and a command name, this returns a + :class:`Command` object if it exists or returns `None`. + """ + raise NotImplementedError + + def list_commands(self, ctx: Context) -> t.List[str]: + """Returns a list of subcommand names in the order they should + appear. + """ + return [] + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. Looks + at the names of options, subcommands, and chained + multi-commands. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + results = [ + CompletionItem(name, help=command.get_short_help_str()) + for name, command in _complete_visible_commands(ctx, incomplete) + ] + results.extend(super().shell_complete(ctx, incomplete)) + return results + + +class Group(MultiCommand): + """A group allows a command to have subcommands attached. This is + the most common way to implement nesting in Click. + + :param name: The name of the group command. + :param commands: A dict mapping names to :class:`Command` objects. + Can also be a list of :class:`Command`, which will use + :attr:`Command.name` to create the dict. + :param attrs: Other command arguments described in + :class:`MultiCommand`, :class:`Command`, and + :class:`BaseCommand`. + + .. versionchanged:: 8.0 + The ``commmands`` argument can be a list of command objects. + """ + + #: If set, this is used by the group's :meth:`command` decorator + #: as the default :class:`Command` class. This is useful to make all + #: subcommands use a custom command class. + #: + #: .. versionadded:: 8.0 + command_class: t.Optional[t.Type[Command]] = None + + #: If set, this is used by the group's :meth:`group` decorator + #: as the default :class:`Group` class. This is useful to make all + #: subgroups use a custom group class. + #: + #: If set to the special value :class:`type` (literally + #: ``group_class = type``), this group's class will be used as the + #: default class. This makes a custom group class continue to make + #: custom groups. + #: + #: .. versionadded:: 8.0 + group_class: t.Optional[t.Union[t.Type["Group"], t.Type[type]]] = None + # Literal[type] isn't valid, so use Type[type] + + def __init__( + self, + name: t.Optional[str] = None, + commands: t.Optional[t.Union[t.Dict[str, Command], t.Sequence[Command]]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + + if commands is None: + commands = {} + elif isinstance(commands, abc.Sequence): + commands = {c.name: c for c in commands if c.name is not None} + + #: The registered subcommands by their exported names. + self.commands: t.Dict[str, Command] = commands + + def add_command(self, cmd: Command, name: t.Optional[str] = None) -> None: + """Registers another :class:`Command` with this group. If the name + is not provided, the name of the command is used. + """ + name = name or cmd.name + if name is None: + raise TypeError("Command has no name.") + _check_multicommand(self, name, cmd, register=True) + self.commands[name] = cmd + + def command( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], Command]: + """A shortcut decorator for declaring and attaching a command to + the group. This takes the same arguments as :func:`command` and + immediately registers the created command with this group by + calling :meth:`add_command`. + + To customize the command class used, set the + :attr:`command_class` attribute. + + .. versionchanged:: 8.0 + Added the :attr:`command_class` attribute. + """ + from .decorators import command + + if self.command_class is not None and "cls" not in kwargs: + kwargs["cls"] = self.command_class + + def decorator(f: t.Callable[..., t.Any]) -> Command: + cmd = command(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + return decorator + + def group( + self, *args: t.Any, **kwargs: t.Any + ) -> t.Callable[[t.Callable[..., t.Any]], "Group"]: + """A shortcut decorator for declaring and attaching a group to + the group. This takes the same arguments as :func:`group` and + immediately registers the created group with this group by + calling :meth:`add_command`. + + To customize the group class used, set the :attr:`group_class` + attribute. + + .. versionchanged:: 8.0 + Added the :attr:`group_class` attribute. + """ + from .decorators import group + + if self.group_class is not None and "cls" not in kwargs: + if self.group_class is type: + kwargs["cls"] = type(self) + else: + kwargs["cls"] = self.group_class + + def decorator(f: t.Callable[..., t.Any]) -> "Group": + cmd = group(*args, **kwargs)(f) + self.add_command(cmd) + return cmd + + return decorator + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + return self.commands.get(cmd_name) + + def list_commands(self, ctx: Context) -> t.List[str]: + return sorted(self.commands) + + +class CommandCollection(MultiCommand): + """A command collection is a multi command that merges multiple multi + commands together into one. This is a straightforward implementation + that accepts a list of different multi commands as sources and + provides all the commands for each of them. + """ + + def __init__( + self, + name: t.Optional[str] = None, + sources: t.Optional[t.List[MultiCommand]] = None, + **attrs: t.Any, + ) -> None: + super().__init__(name, **attrs) + #: The list of registered multi commands. + self.sources: t.List[MultiCommand] = sources or [] + + def add_source(self, multi_cmd: MultiCommand) -> None: + """Adds a new multi command to the chain dispatcher.""" + self.sources.append(multi_cmd) + + def get_command(self, ctx: Context, cmd_name: str) -> t.Optional[Command]: + for source in self.sources: + rv = source.get_command(ctx, cmd_name) + + if rv is not None: + if self.chain: + _check_multicommand(self, cmd_name, rv) + + return rv + + return None + + def list_commands(self, ctx: Context) -> t.List[str]: + rv: t.Set[str] = set() + + for source in self.sources: + rv.update(source.list_commands(ctx)) + + return sorted(rv) + + +def _check_iter(value: t.Any) -> t.Iterator[t.Any]: + """Check if the value is iterable but not a string. Raises a type + error, or return an iterator over the value. + """ + if isinstance(value, str): + raise TypeError + + return iter(value) + + +class Parameter: + r"""A parameter to a command comes in two versions: they are either + :class:`Option`\s or :class:`Argument`\s. Other subclasses are currently + not supported by design as some of the internals for parsing are + intentionally not finalized. + + Some settings are supported by both options and arguments. + + :param param_decls: the parameter declarations for this option or + argument. This is a list of flags or argument + names. + :param type: the type that should be used. Either a :class:`ParamType` + or a Python type. The later is converted into the former + automatically if supported. + :param required: controls if this is optional or not. + :param default: the default value if omitted. This can also be a callable, + in which case it's invoked when the default is needed + without any arguments. + :param callback: A function to further process or validate the value + after type conversion. It is called as ``f(ctx, param, value)`` + and must return the value. It is called for all sources, + including prompts. + :param nargs: the number of arguments to match. If not ``1`` the return + value is a tuple instead of single value. The default for + nargs is ``1`` (except if the type is a tuple, then it's + the arity of the tuple). If ``nargs=-1``, all remaining + parameters are collected. + :param metavar: how the value is represented in the help page. + :param expose_value: if this is `True` then the value is passed onwards + to the command callback and stored on the context, + otherwise it's skipped. + :param is_eager: eager values are processed before non eager ones. This + should not be set for arguments or it will inverse the + order of processing. + :param envvar: a string or list of strings that are environment variables + that should be checked. + :param shell_complete: A function that returns custom shell + completions. Used instead of the param's type completion if + given. Takes ``ctx, param, incomplete`` and must return a list + of :class:`~click.shell_completion.CompletionItem` or a list of + strings. + + .. versionchanged:: 8.0 + ``process_value`` validates required parameters and bounded + ``nargs``, and invokes the parameter callback before returning + the value. This allows the callback to validate prompts. + ``full_process_value`` is removed. + + .. versionchanged:: 8.0 + ``autocompletion`` is renamed to ``shell_complete`` and has new + semantics described above. The old name is deprecated and will + be removed in 8.1, until then it will be wrapped to match the + new requirements. + + .. versionchanged:: 8.0 + For ``multiple=True, nargs>1``, the default must be a list of + tuples. + + .. versionchanged:: 8.0 + Setting a default is no longer required for ``nargs>1``, it will + default to ``None``. ``multiple=True`` or ``nargs=-1`` will + default to ``()``. + + .. versionchanged:: 7.1 + Empty environment variables are ignored rather than taking the + empty string value. This makes it possible for scripts to clear + variables if they can't unset them. + + .. versionchanged:: 2.0 + Changed signature for parameter callback to also be passed the + parameter. The old callback format will still work, but it will + raise a warning to give you a chance to migrate the code easier. + """ + + param_type_name = "parameter" + + def __init__( + self, + param_decls: t.Optional[t.Sequence[str]] = None, + type: t.Optional[t.Union[types.ParamType, t.Any]] = None, + required: bool = False, + default: t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]] = None, + callback: t.Optional[t.Callable[[Context, "Parameter", t.Any], t.Any]] = None, + nargs: t.Optional[int] = None, + multiple: bool = False, + metavar: t.Optional[str] = None, + expose_value: bool = True, + is_eager: bool = False, + envvar: t.Optional[t.Union[str, t.Sequence[str]]] = None, + shell_complete: t.Optional[ + t.Callable[ + [Context, "Parameter", str], + t.Union[t.List["CompletionItem"], t.List[str]], + ] + ] = None, + autocompletion: t.Optional[ + t.Callable[ + [Context, t.List[str], str], t.List[t.Union[t.Tuple[str, str], str]] + ] + ] = None, + ) -> None: + self.name, self.opts, self.secondary_opts = self._parse_decls( + param_decls or (), expose_value + ) + self.type = types.convert_type(type, default) + + # Default nargs to what the type tells us if we have that + # information available. + if nargs is None: + if self.type.is_composite: + nargs = self.type.arity + else: + nargs = 1 + + self.required = required + self.callback = callback + self.nargs = nargs + self.multiple = multiple + self.expose_value = expose_value + self.default = default + self.is_eager = is_eager + self.metavar = metavar + self.envvar = envvar + + if autocompletion is not None: + import warnings + + warnings.warn( + "'autocompletion' is renamed to 'shell_complete'. The old name is" + " deprecated and will be removed in Click 8.1. See the docs about" + " 'Parameter' for information about new behavior.", + DeprecationWarning, + stacklevel=2, + ) + + def shell_complete( + ctx: Context, param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + from click.shell_completion import CompletionItem + + out = [] + + for c in autocompletion(ctx, [], incomplete): # type: ignore + if isinstance(c, tuple): + c = CompletionItem(c[0], help=c[1]) + elif isinstance(c, str): + c = CompletionItem(c) + + if c.value.startswith(incomplete): + out.append(c) + + return out + + self._custom_shell_complete = shell_complete + + if __debug__: + if self.type.is_composite and nargs != self.type.arity: + raise ValueError( + f"'nargs' must be {self.type.arity} (or None) for" + f" type {self.type!r}, but it was {nargs}." + ) + + # Skip no default or callable default. + check_default = default if not callable(default) else None + + if check_default is not None: + if multiple: + try: + # Only check the first value against nargs. + check_default = next(_check_iter(check_default), None) + except TypeError: + raise ValueError( + "'default' must be a list when 'multiple' is true." + ) from None + + # Can be None for multiple with empty default. + if nargs != 1 and check_default is not None: + try: + _check_iter(check_default) + except TypeError: + if multiple: + message = ( + "'default' must be a list of lists when 'multiple' is" + " true and 'nargs' != 1." + ) + else: + message = "'default' must be a list when 'nargs' != 1." + + raise ValueError(message) from None + + if nargs > 1 and len(check_default) != nargs: + subject = "item length" if multiple else "length" + raise ValueError( + f"'default' {subject} must match nargs={nargs}." + ) + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + return { + "name": self.name, + "param_type_name": self.param_type_name, + "opts": self.opts, + "secondary_opts": self.secondary_opts, + "type": self.type.to_info_dict(), + "required": self.required, + "nargs": self.nargs, + "multiple": self.multiple, + "default": self.default, + "envvar": self.envvar, + } + + def __repr__(self) -> str: + return f"<{self.__class__.__name__} {self.name}>" + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + raise NotImplementedError() + + @property + def human_readable_name(self) -> str: + """Returns the human readable name of this parameter. This is the + same as the name for options, but the metavar for arguments. + """ + return self.name # type: ignore + + def make_metavar(self) -> str: + if self.metavar is not None: + return self.metavar + + metavar = self.type.get_metavar(self) + + if metavar is None: + metavar = self.type.name.upper() + + if self.nargs != 1: + metavar += "..." + + return metavar + + @typing.overload + def get_default( + self, ctx: Context, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @typing.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + """Get the default for the parameter. Tries + :meth:`Context.lookup_default` first, then the local default. + + :param ctx: Current context. + :param call: If the default is a callable, call it. Disable to + return the callable instead. + + .. versionchanged:: 8.0.2 + Type casting is no longer performed when getting a default. + + .. versionchanged:: 8.0.1 + Type casting can fail in resilient parsing mode. Invalid + defaults will not prevent showing help text. + + .. versionchanged:: 8.0 + Looks at ``ctx.default_map`` first. + + .. versionchanged:: 8.0 + Added the ``call`` parameter. + """ + value = ctx.lookup_default(self.name, call=False) # type: ignore + + if value is None: + value = self.default + + if call and callable(value): + value = value() + + return value + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + raise NotImplementedError() + + def consume_value( + self, ctx: Context, opts: t.Mapping[str, t.Any] + ) -> t.Tuple[t.Any, ParameterSource]: + value = opts.get(self.name) # type: ignore + source = ParameterSource.COMMANDLINE + + if value is None: + value = self.value_from_envvar(ctx) + source = ParameterSource.ENVIRONMENT + + if value is None: + value = ctx.lookup_default(self.name) # type: ignore + source = ParameterSource.DEFAULT_MAP + + if value is None: + value = self.get_default(ctx) + source = ParameterSource.DEFAULT + + return value, source + + def type_cast_value(self, ctx: Context, value: t.Any) -> t.Any: + """Convert and validate a value against the option's + :attr:`type`, :attr:`multiple`, and :attr:`nargs`. + """ + if value is None: + return () if self.multiple or self.nargs == -1 else None + + def check_iter(value: t.Any) -> t.Iterator: + try: + return _check_iter(value) + except TypeError: + # This should only happen when passing in args manually, + # the parser should construct an iterable when parsing + # the command line. + raise BadParameter( + _("Value must be an iterable."), ctx=ctx, param=self + ) from None + + if self.nargs == 1 or self.type.is_composite: + convert: t.Callable[[t.Any], t.Any] = partial( + self.type, param=self, ctx=ctx + ) + elif self.nargs == -1: + + def convert(value: t.Any) -> t.Tuple: + return tuple(self.type(x, self, ctx) for x in check_iter(value)) + + else: # nargs > 1 + + def convert(value: t.Any) -> t.Tuple: + value = tuple(check_iter(value)) + + if len(value) != self.nargs: + raise BadParameter( + ngettext( + "Takes {nargs} values but 1 was given.", + "Takes {nargs} values but {len} were given.", + len(value), + ).format(nargs=self.nargs, len=len(value)), + ctx=ctx, + param=self, + ) + + return tuple(self.type(x, self, ctx) for x in value) + + if self.multiple: + return tuple(convert(x) for x in check_iter(value)) + + return convert(value) + + def value_is_missing(self, value: t.Any) -> bool: + if value is None: + return True + + if (self.nargs != 1 or self.multiple) and value == (): + return True + + return False + + def process_value(self, ctx: Context, value: t.Any) -> t.Any: + value = self.type_cast_value(ctx, value) + + if self.required and self.value_is_missing(value): + raise MissingParameter(ctx=ctx, param=self) + + if self.callback is not None: + value = self.callback(ctx, self, value) + + return value + + def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: + if self.envvar is None: + return None + + if isinstance(self.envvar, str): + rv = os.environ.get(self.envvar) + + if rv: + return rv + else: + for envvar in self.envvar: + rv = os.environ.get(envvar) + + if rv: + return rv + + return None + + def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: + rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) + + if rv is not None and self.nargs != 1: + rv = self.type.split_envvar_value(rv) + + return rv + + def handle_parse_result( + self, ctx: Context, opts: t.Mapping[str, t.Any], args: t.List[str] + ) -> t.Tuple[t.Any, t.List[str]]: + with augment_usage_errors(ctx, param=self): + value, source = self.consume_value(ctx, opts) + ctx.set_parameter_source(self.name, source) # type: ignore + + try: + value = self.process_value(ctx, value) + except Exception: + if not ctx.resilient_parsing: + raise + + value = None + + if self.expose_value: + ctx.params[self.name] = value # type: ignore + + return value, args + + def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: + pass + + def get_usage_pieces(self, ctx: Context) -> t.List[str]: + return [] + + def get_error_hint(self, ctx: Context) -> str: + """Get a stringified version of the param for use in error messages to + indicate which param caused the error. + """ + hint_list = self.opts or [self.human_readable_name] + return " / ".join(f"'{x}'" for x in hint_list) + + def shell_complete(self, ctx: Context, incomplete: str) -> t.List["CompletionItem"]: + """Return a list of completions for the incomplete value. If a + ``shell_complete`` function was given during init, it is used. + Otherwise, the :attr:`type` + :meth:`~click.types.ParamType.shell_complete` function is used. + + :param ctx: Invocation context for this command. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + if self._custom_shell_complete is not None: + results = self._custom_shell_complete(ctx, self, incomplete) + + if results and isinstance(results[0], str): + from click.shell_completion import CompletionItem + + results = [CompletionItem(c) for c in results] + + return t.cast(t.List["CompletionItem"], results) + + return self.type.shell_complete(ctx, self, incomplete) + + +class Option(Parameter): + """Options are usually optional values on the command line and + have some extra features that arguments don't have. + + All other parameters are passed onwards to the parameter constructor. + + :param show_default: controls if the default value should be shown on the + help page. Normally, defaults are not shown. If this + value is a string, it shows the string instead of the + value. This is particularly useful for dynamic options. + :param show_envvar: controls if an environment variable should be shown on + the help page. Normally, environment variables + are not shown. + :param prompt: if set to `True` or a non empty string then the user will be + prompted for input. If set to `True` the prompt will be the + option name capitalized. + :param confirmation_prompt: Prompt a second time to confirm the + value if it was prompted for. Can be set to a string instead of + ``True`` to customize the message. + :param prompt_required: If set to ``False``, the user will be + prompted for input only when the option was specified as a flag + without a value. + :param hide_input: if this is `True` then the input on the prompt will be + hidden from the user. This is useful for password + input. + :param is_flag: forces this option to act as a flag. The default is + auto detection. + :param flag_value: which value should be used for this flag if it's + enabled. This is set to a boolean automatically if + the option string contains a slash to mark two options. + :param multiple: if this is set to `True` then the argument is accepted + multiple times and recorded. This is similar to ``nargs`` + in how it works but supports arbitrary number of + arguments. + :param count: this flag makes an option increment an integer. + :param allow_from_autoenv: if this is enabled then the value of this + parameter will be pulled from an environment + variable in case a prefix is defined on the + context. + :param help: the help string. + :param hidden: hide this option from help outputs. + + .. versionchanged:: 8.0.1 + ``type`` is detected from ``flag_value`` if given. + """ + + param_type_name = "option" + + def __init__( + self, + param_decls: t.Optional[t.Sequence[str]] = None, + show_default: t.Union[bool, str] = False, + prompt: t.Union[bool, str] = False, + confirmation_prompt: t.Union[bool, str] = False, + prompt_required: bool = True, + hide_input: bool = False, + is_flag: t.Optional[bool] = None, + flag_value: t.Optional[t.Any] = None, + multiple: bool = False, + count: bool = False, + allow_from_autoenv: bool = True, + type: t.Optional[t.Union[types.ParamType, t.Any]] = None, + help: t.Optional[str] = None, + hidden: bool = False, + show_choices: bool = True, + show_envvar: bool = False, + **attrs: t.Any, + ) -> None: + default_is_missing = "default" not in attrs + super().__init__(param_decls, type=type, multiple=multiple, **attrs) + + if prompt is True: + if self.name is None: + raise TypeError("'name' is required with 'prompt=True'.") + + prompt_text: t.Optional[str] = self.name.replace("_", " ").capitalize() + elif prompt is False: + prompt_text = None + else: + prompt_text = t.cast(str, prompt) + + self.prompt = prompt_text + self.confirmation_prompt = confirmation_prompt + self.prompt_required = prompt_required + self.hide_input = hide_input + self.hidden = hidden + + # If prompt is enabled but not required, then the option can be + # used as a flag to indicate using prompt or flag_value. + self._flag_needs_value = self.prompt is not None and not self.prompt_required + + if is_flag is None: + if flag_value is not None: + # Implicitly a flag because flag_value was set. + is_flag = True + elif self._flag_needs_value: + # Not a flag, but when used as a flag it shows a prompt. + is_flag = False + else: + # Implicitly a flag because flag options were given. + is_flag = bool(self.secondary_opts) + elif is_flag is False and not self._flag_needs_value: + # Not a flag, and prompt is not enabled, can be used as a + # flag if flag_value is set. + self._flag_needs_value = flag_value is not None + + if is_flag and default_is_missing: + self.default: t.Union[t.Any, t.Callable[[], t.Any]] = False + + if flag_value is None: + flag_value = not self.default + + if is_flag and type is None: + # Re-guess the type from the flag value instead of the + # default. + self.type = types.convert_type(None, flag_value) + + self.is_flag: bool = is_flag + self.is_bool_flag = is_flag and isinstance(self.type, types.BoolParamType) + self.flag_value: t.Any = flag_value + + # Counting + self.count = count + if count: + if type is None: + self.type = types.IntRange(min=0) + if default_is_missing: + self.default = 0 + + self.allow_from_autoenv = allow_from_autoenv + self.help = help + self.show_default = show_default + self.show_choices = show_choices + self.show_envvar = show_envvar + + if __debug__: + if self.nargs == -1: + raise TypeError("nargs=-1 is not supported for options.") + + if self.prompt and self.is_flag and not self.is_bool_flag: + raise TypeError("'prompt' is not valid for non-boolean flag.") + + if not self.is_bool_flag and self.secondary_opts: + raise TypeError("Secondary flag is not valid for non-boolean flag.") + + if self.is_bool_flag and self.hide_input and self.prompt is not None: + raise TypeError( + "'prompt' with 'hide_input' is not valid for boolean flag." + ) + + if self.count: + if self.multiple: + raise TypeError("'count' is not valid with 'multiple'.") + + if self.is_flag: + raise TypeError("'count' is not valid with 'is_flag'.") + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + help=self.help, + prompt=self.prompt, + is_flag=self.is_flag, + flag_value=self.flag_value, + count=self.count, + hidden=self.hidden, + ) + return info_dict + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + opts = [] + secondary_opts = [] + name = None + possible_names = [] + + for decl in decls: + if decl.isidentifier(): + if name is not None: + raise TypeError(f"Name '{name}' defined twice") + name = decl + else: + split_char = ";" if decl[:1] == "/" else "/" + if split_char in decl: + first, second = decl.split(split_char, 1) + first = first.rstrip() + if first: + possible_names.append(split_opt(first)) + opts.append(first) + second = second.lstrip() + if second: + secondary_opts.append(second.lstrip()) + if first == second: + raise ValueError( + f"Boolean option {decl!r} cannot use the" + " same flag for true/false." + ) + else: + possible_names.append(split_opt(decl)) + opts.append(decl) + + if name is None and possible_names: + possible_names.sort(key=lambda x: -len(x[0])) # group long options first + name = possible_names[0][1].replace("-", "_").lower() + if not name.isidentifier(): + name = None + + if name is None: + if not expose_value: + return None, opts, secondary_opts + raise TypeError("Could not determine name for option") + + if not opts and not secondary_opts: + raise TypeError( + f"No options defined but a name was passed ({name})." + " Did you mean to declare an argument instead? Did" + f" you mean to pass '--{name}'?" + ) + + return name, opts, secondary_opts + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + if self.multiple: + action = "append" + elif self.count: + action = "count" + else: + action = "store" + + if self.is_flag: + action = f"{action}_const" + + if self.is_bool_flag and self.secondary_opts: + parser.add_option( + obj=self, opts=self.opts, dest=self.name, action=action, const=True + ) + parser.add_option( + obj=self, + opts=self.secondary_opts, + dest=self.name, + action=action, + const=False, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + const=self.flag_value, + ) + else: + parser.add_option( + obj=self, + opts=self.opts, + dest=self.name, + action=action, + nargs=self.nargs, + ) + + def get_help_record(self, ctx: Context) -> t.Optional[t.Tuple[str, str]]: + if self.hidden: + return None + + any_prefix_is_slash = False + + def _write_opts(opts: t.Sequence[str]) -> str: + nonlocal any_prefix_is_slash + + rv, any_slashes = join_options(opts) + + if any_slashes: + any_prefix_is_slash = True + + if not self.is_flag and not self.count: + rv += f" {self.make_metavar()}" + + return rv + + rv = [_write_opts(self.opts)] + + if self.secondary_opts: + rv.append(_write_opts(self.secondary_opts)) + + help = self.help or "" + extra = [] + + if self.show_envvar: + envvar = self.envvar + + if envvar is None: + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + + if envvar is not None: + var_str = ( + envvar + if isinstance(envvar, str) + else ", ".join(str(d) for d in envvar) + ) + extra.append(_("env var: {var}").format(var=var_str)) + + # Temporarily enable resilient parsing to avoid type casting + # failing for the default. Might be possible to extend this to + # help formatting in general. + resilient = ctx.resilient_parsing + ctx.resilient_parsing = True + + try: + default_value = self.get_default(ctx, call=False) + finally: + ctx.resilient_parsing = resilient + + show_default_is_str = isinstance(self.show_default, str) + + if show_default_is_str or ( + default_value is not None and (self.show_default or ctx.show_default) + ): + if show_default_is_str: + default_string = f"({self.show_default})" + elif isinstance(default_value, (list, tuple)): + default_string = ", ".join(str(d) for d in default_value) + elif callable(default_value): + default_string = _("(dynamic)") + elif self.is_bool_flag and self.secondary_opts: + # For boolean flags that have distinct True/False opts, + # use the opt without prefix instead of the value. + default_string = split_opt( + (self.opts if self.default else self.secondary_opts)[0] + )[1] + else: + default_string = str(default_value) + + if default_string: + extra.append(_("default: {default}").format(default=default_string)) + + if ( + isinstance(self.type, types._NumberRangeBase) + # skip count with default range type + and not (self.count and self.type.min == 0 and self.type.max is None) + ): + range_str = self.type._describe_range() + + if range_str: + extra.append(range_str) + + if self.required: + extra.append(_("required")) + + if extra: + extra_str = "; ".join(extra) + help = f"{help} [{extra_str}]" if help else f"[{extra_str}]" + + return ("; " if any_prefix_is_slash else " / ").join(rv), help + + @typing.overload + def get_default( + self, ctx: Context, call: "te.Literal[True]" = True + ) -> t.Optional[t.Any]: + ... + + @typing.overload + def get_default( + self, ctx: Context, call: bool = ... + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + ... + + def get_default( + self, ctx: Context, call: bool = True + ) -> t.Optional[t.Union[t.Any, t.Callable[[], t.Any]]]: + # If we're a non boolean flag our default is more complex because + # we need to look at all flags in the same group to figure out + # if we're the the default one in which case we return the flag + # value as default. + if self.is_flag and not self.is_bool_flag: + for param in ctx.command.params: + if param.name == self.name and param.default: + return param.flag_value # type: ignore + + return None + + return super().get_default(ctx, call=call) + + def prompt_for_value(self, ctx: Context) -> t.Any: + """This is an alternative flow that can be activated in the full + value processing if a value does not exist. It will prompt the + user until a valid value exists and then returns the processed + value as result. + """ + assert self.prompt is not None + + # Calculate the default before prompting anything to be stable. + default = self.get_default(ctx) + + # If this is a prompt for a flag we need to handle this + # differently. + if self.is_bool_flag: + return confirm(self.prompt, default) + + return prompt( + self.prompt, + default=default, + type=self.type, + hide_input=self.hide_input, + show_choices=self.show_choices, + confirmation_prompt=self.confirmation_prompt, + value_proc=lambda x: self.process_value(ctx, x), + ) + + def resolve_envvar_value(self, ctx: Context) -> t.Optional[str]: + rv = super().resolve_envvar_value(ctx) + + if rv is not None: + return rv + + if ( + self.allow_from_autoenv + and ctx.auto_envvar_prefix is not None + and self.name is not None + ): + envvar = f"{ctx.auto_envvar_prefix}_{self.name.upper()}" + rv = os.environ.get(envvar) + + return rv + + def value_from_envvar(self, ctx: Context) -> t.Optional[t.Any]: + rv: t.Optional[t.Any] = self.resolve_envvar_value(ctx) + + if rv is None: + return None + + value_depth = (self.nargs != 1) + bool(self.multiple) + + if value_depth > 0: + rv = self.type.split_envvar_value(rv) + + if self.multiple and self.nargs != 1: + rv = batch(rv, self.nargs) + + return rv + + def consume_value( + self, ctx: Context, opts: t.Mapping[str, "Parameter"] + ) -> t.Tuple[t.Any, ParameterSource]: + value, source = super().consume_value(ctx, opts) + + # The parser will emit a sentinel value if the option can be + # given as a flag without a value. This is different from None + # to distinguish from the flag not being given at all. + if value is _flag_needs_value: + if self.prompt is not None and not ctx.resilient_parsing: + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + else: + value = self.flag_value + source = ParameterSource.COMMANDLINE + + elif ( + self.multiple + and value is not None + and any(v is _flag_needs_value for v in value) + ): + value = [self.flag_value if v is _flag_needs_value else v for v in value] + source = ParameterSource.COMMANDLINE + + # The value wasn't set, or used the param's default, prompt if + # prompting is enabled. + elif ( + source in {None, ParameterSource.DEFAULT} + and self.prompt is not None + and (self.required or self.prompt_required) + and not ctx.resilient_parsing + ): + value = self.prompt_for_value(ctx) + source = ParameterSource.PROMPT + + return value, source + + +class Argument(Parameter): + """Arguments are positional parameters to a command. They generally + provide fewer features than options but can have infinite ``nargs`` + and are required by default. + + All parameters are passed onwards to the parameter constructor. + """ + + param_type_name = "argument" + + def __init__( + self, + param_decls: t.Sequence[str], + required: t.Optional[bool] = None, + **attrs: t.Any, + ) -> None: + if required is None: + if attrs.get("default") is not None: + required = False + else: + required = attrs.get("nargs", 1) > 0 + + if "multiple" in attrs: + raise TypeError("__init__() got an unexpected keyword argument 'multiple'.") + + super().__init__(param_decls, required=required, **attrs) + + if __debug__: + if self.default is not None and self.nargs == -1: + raise TypeError("'default' is not supported for nargs=-1.") + + @property + def human_readable_name(self) -> str: + if self.metavar is not None: + return self.metavar + return self.name.upper() # type: ignore + + def make_metavar(self) -> str: + if self.metavar is not None: + return self.metavar + var = self.type.get_metavar(self) + if not var: + var = self.name.upper() # type: ignore + if not self.required: + var = f"[{var}]" + if self.nargs != 1: + var += "..." + return var + + def _parse_decls( + self, decls: t.Sequence[str], expose_value: bool + ) -> t.Tuple[t.Optional[str], t.List[str], t.List[str]]: + if not decls: + if not expose_value: + return None, [], [] + raise TypeError("Could not determine name for argument") + if len(decls) == 1: + name = arg = decls[0] + name = name.replace("-", "_").lower() + else: + raise TypeError( + "Arguments take exactly one parameter declaration, got" + f" {len(decls)}." + ) + return name, [arg], [] + + def get_usage_pieces(self, ctx: Context) -> t.List[str]: + return [self.make_metavar()] + + def get_error_hint(self, ctx: Context) -> str: + return f"'{self.make_metavar()}'" + + def add_to_parser(self, parser: OptionParser, ctx: Context) -> None: + parser.add_argument(dest=self.name, nargs=self.nargs, obj=self) diff --git a/flask-app/venv/lib/python3.8/site-packages/click/decorators.py b/flask-app/venv/lib/python3.8/site-packages/click/decorators.py new file mode 100644 index 000000000..f1cc005af --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click/decorators.py @@ -0,0 +1,436 @@ +import inspect +import types +import typing as t +from functools import update_wrapper +from gettext import gettext as _ + +from .core import Argument +from .core import Command +from .core import Context +from .core import Group +from .core import Option +from .core import Parameter +from .globals import get_current_context +from .utils import echo + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) +FC = t.TypeVar("FC", t.Callable[..., t.Any], Command) + + +def pass_context(f: F) -> F: + """Marks a callback as wanting to receive the current context + object as first argument. + """ + + def new_func(*args, **kwargs): # type: ignore + return f(get_current_context(), *args, **kwargs) + + return update_wrapper(t.cast(F, new_func), f) + + +def pass_obj(f: F) -> F: + """Similar to :func:`pass_context`, but only pass the object on the + context onwards (:attr:`Context.obj`). This is useful if that object + represents the state of a nested system. + """ + + def new_func(*args, **kwargs): # type: ignore + return f(get_current_context().obj, *args, **kwargs) + + return update_wrapper(t.cast(F, new_func), f) + + +def make_pass_decorator( + object_type: t.Type, ensure: bool = False +) -> "t.Callable[[F], F]": + """Given an object type this creates a decorator that will work + similar to :func:`pass_obj` but instead of passing the object of the + current context, it will find the innermost context of type + :func:`object_type`. + + This generates a decorator that works roughly like this:: + + from functools import update_wrapper + + def decorator(f): + @pass_context + def new_func(ctx, *args, **kwargs): + obj = ctx.find_object(object_type) + return ctx.invoke(f, obj, *args, **kwargs) + return update_wrapper(new_func, f) + return decorator + + :param object_type: the type of the object to pass. + :param ensure: if set to `True`, a new object will be created and + remembered on the context if it's not there yet. + """ + + def decorator(f: F) -> F: + def new_func(*args, **kwargs): # type: ignore + ctx = get_current_context() + + if ensure: + obj = ctx.ensure_object(object_type) + else: + obj = ctx.find_object(object_type) + + if obj is None: + raise RuntimeError( + "Managed to invoke callback without a context" + f" object of type {object_type.__name__!r}" + " existing." + ) + + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(t.cast(F, new_func), f) + + return decorator + + +def pass_meta_key( + key: str, *, doc_description: t.Optional[str] = None +) -> "t.Callable[[F], F]": + """Create a decorator that passes a key from + :attr:`click.Context.meta` as the first argument to the decorated + function. + + :param key: Key in ``Context.meta`` to pass. + :param doc_description: Description of the object being passed, + inserted into the decorator's docstring. Defaults to "the 'key' + key from Context.meta". + + .. versionadded:: 8.0 + """ + + def decorator(f: F) -> F: + def new_func(*args, **kwargs): # type: ignore + ctx = get_current_context() + obj = ctx.meta[key] + return ctx.invoke(f, obj, *args, **kwargs) + + return update_wrapper(t.cast(F, new_func), f) + + if doc_description is None: + doc_description = f"the {key!r} key from :attr:`click.Context.meta`" + + decorator.__doc__ = ( + f"Decorator that passes {doc_description} as the first argument" + " to the decorated function." + ) + return decorator + + +def _make_command( + f: F, + name: t.Optional[str], + attrs: t.MutableMapping[str, t.Any], + cls: t.Type[Command], +) -> Command: + if isinstance(f, Command): + raise TypeError("Attempted to convert a callback into a command twice.") + + try: + params = f.__click_params__ # type: ignore + params.reverse() + del f.__click_params__ # type: ignore + except AttributeError: + params = [] + + help = attrs.get("help") + + if help is None: + help = inspect.getdoc(f) + else: + help = inspect.cleandoc(help) + + attrs["help"] = help + return cls( + name=name or f.__name__.lower().replace("_", "-"), + callback=f, + params=params, + **attrs, + ) + + +def command( + name: t.Optional[str] = None, + cls: t.Optional[t.Type[Command]] = None, + **attrs: t.Any, +) -> t.Callable[[F], Command]: + r"""Creates a new :class:`Command` and uses the decorated function as + callback. This will also automatically attach all decorated + :func:`option`\s and :func:`argument`\s as parameters to the command. + + The name of the command defaults to the name of the function with + underscores replaced by dashes. If you want to change that, you can + pass the intended name as the first argument. + + All keyword arguments are forwarded to the underlying command class. + + Once decorated the function turns into a :class:`Command` instance + that can be invoked as a command line utility or be attached to a + command :class:`Group`. + + :param name: the name of the command. This defaults to the function + name with underscores replaced by dashes. + :param cls: the command class to instantiate. This defaults to + :class:`Command`. + """ + if cls is None: + cls = Command + + def decorator(f: t.Callable[..., t.Any]) -> Command: + cmd = _make_command(f, name, attrs, cls) # type: ignore + cmd.__doc__ = f.__doc__ + return cmd + + return decorator + + +def group(name: t.Optional[str] = None, **attrs: t.Any) -> t.Callable[[F], Group]: + """Creates a new :class:`Group` with a function as callback. This + works otherwise the same as :func:`command` just that the `cls` + parameter is set to :class:`Group`. + """ + attrs.setdefault("cls", Group) + return t.cast(Group, command(name, **attrs)) + + +def _param_memo(f: FC, param: Parameter) -> None: + if isinstance(f, Command): + f.params.append(param) + else: + if not hasattr(f, "__click_params__"): + f.__click_params__ = [] # type: ignore + + f.__click_params__.append(param) # type: ignore + + +def argument(*param_decls: str, **attrs: t.Any) -> t.Callable[[FC], FC]: + """Attaches an argument to the command. All positional arguments are + passed as parameter declarations to :class:`Argument`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Argument` instance manually + and attaching it to the :attr:`Command.params` list. + + :param cls: the argument class to instantiate. This defaults to + :class:`Argument`. + """ + + def decorator(f: FC) -> FC: + ArgumentClass = attrs.pop("cls", Argument) + _param_memo(f, ArgumentClass(param_decls, **attrs)) + return f + + return decorator + + +def option(*param_decls: str, **attrs: t.Any) -> t.Callable[[FC], FC]: + """Attaches an option to the command. All positional arguments are + passed as parameter declarations to :class:`Option`; all keyword + arguments are forwarded unchanged (except ``cls``). + This is equivalent to creating an :class:`Option` instance manually + and attaching it to the :attr:`Command.params` list. + + :param cls: the option class to instantiate. This defaults to + :class:`Option`. + """ + + def decorator(f: FC) -> FC: + # Issue 926, copy attrs, so pre-defined options can re-use the same cls= + option_attrs = attrs.copy() + + if "help" in option_attrs: + option_attrs["help"] = inspect.cleandoc(option_attrs["help"]) + OptionClass = option_attrs.pop("cls", Option) + _param_memo(f, OptionClass(param_decls, **option_attrs)) + return f + + return decorator + + +def confirmation_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--yes`` option which shows a prompt before continuing if + not passed. If the prompt is declined, the program will exit. + + :param param_decls: One or more option names. Defaults to the single + value ``"--yes"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value: + ctx.abort() + + if not param_decls: + param_decls = ("--yes",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("callback", callback) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("prompt", "Do you want to continue?") + kwargs.setdefault("help", "Confirm the action without prompting.") + return option(*param_decls, **kwargs) + + +def password_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--password`` option which prompts for a password, hiding + input and asking to enter the value again for confirmation. + + :param param_decls: One or more option names. Defaults to the single + value ``"--password"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + if not param_decls: + param_decls = ("--password",) + + kwargs.setdefault("prompt", True) + kwargs.setdefault("confirmation_prompt", True) + kwargs.setdefault("hide_input", True) + return option(*param_decls, **kwargs) + + +def version_option( + version: t.Optional[str] = None, + *param_decls: str, + package_name: t.Optional[str] = None, + prog_name: t.Optional[str] = None, + message: t.Optional[str] = None, + **kwargs: t.Any, +) -> t.Callable[[FC], FC]: + """Add a ``--version`` option which immediately prints the version + number and exits the program. + + If ``version`` is not provided, Click will try to detect it using + :func:`importlib.metadata.version` to get the version for the + ``package_name``. On Python < 3.8, the ``importlib_metadata`` + backport must be installed. + + If ``package_name`` is not provided, Click will try to detect it by + inspecting the stack frames. This will be used to detect the + version, so it must match the name of the installed package. + + :param version: The version number to show. If not provided, Click + will try to detect it. + :param param_decls: One or more option names. Defaults to the single + value ``"--version"``. + :param package_name: The package name to detect the version from. If + not provided, Click will try to detect it. + :param prog_name: The name of the CLI to show in the message. If not + provided, it will be detected from the command. + :param message: The message to show. The values ``%(prog)s``, + ``%(package)s``, and ``%(version)s`` are available. Defaults to + ``"%(prog)s, version %(version)s"``. + :param kwargs: Extra arguments are passed to :func:`option`. + :raise RuntimeError: ``version`` could not be detected. + + .. versionchanged:: 8.0 + Add the ``package_name`` parameter, and the ``%(package)s`` + value for messages. + + .. versionchanged:: 8.0 + Use :mod:`importlib.metadata` instead of ``pkg_resources``. The + version is detected based on the package name, not the entry + point name. The Python package name must match the installed + package name, or be passed with ``package_name=``. + """ + if message is None: + message = _("%(prog)s, version %(version)s") + + if version is None and package_name is None: + frame = inspect.currentframe() + f_back = frame.f_back if frame is not None else None + f_globals = f_back.f_globals if f_back is not None else None + # break reference cycle + # https://docs.python.org/3/library/inspect.html#the-interpreter-stack + del frame + + if f_globals is not None: + package_name = f_globals.get("__name__") + + if package_name == "__main__": + package_name = f_globals.get("__package__") + + if package_name: + package_name = package_name.partition(".")[0] + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value or ctx.resilient_parsing: + return + + nonlocal prog_name + nonlocal version + + if prog_name is None: + prog_name = ctx.find_root().info_name + + if version is None and package_name is not None: + metadata: t.Optional[types.ModuleType] + + try: + from importlib import metadata # type: ignore + except ImportError: + # Python < 3.8 + import importlib_metadata as metadata # type: ignore + + try: + version = metadata.version(package_name) # type: ignore + except metadata.PackageNotFoundError: # type: ignore + raise RuntimeError( + f"{package_name!r} is not installed. Try passing" + " 'package_name' instead." + ) from None + + if version is None: + raise RuntimeError( + f"Could not determine the version for {package_name!r} automatically." + ) + + echo( + t.cast(str, message) + % {"prog": prog_name, "package": package_name, "version": version}, + color=ctx.color, + ) + ctx.exit() + + if not param_decls: + param_decls = ("--version",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show the version and exit.")) + kwargs["callback"] = callback + return option(*param_decls, **kwargs) + + +def help_option(*param_decls: str, **kwargs: t.Any) -> t.Callable[[FC], FC]: + """Add a ``--help`` option which immediately prints the help page + and exits the program. + + This is usually unnecessary, as the ``--help`` option is added to + each command automatically unless ``add_help_option=False`` is + passed. + + :param param_decls: One or more option names. Defaults to the single + value ``"--help"``. + :param kwargs: Extra arguments are passed to :func:`option`. + """ + + def callback(ctx: Context, param: Parameter, value: bool) -> None: + if not value or ctx.resilient_parsing: + return + + echo(ctx.get_help(), color=ctx.color) + ctx.exit() + + if not param_decls: + param_decls = ("--help",) + + kwargs.setdefault("is_flag", True) + kwargs.setdefault("expose_value", False) + kwargs.setdefault("is_eager", True) + kwargs.setdefault("help", _("Show this message and exit.")) + kwargs["callback"] = callback + return option(*param_decls, **kwargs) diff --git a/flask-app/venv/lib/python3.8/site-packages/click/exceptions.py b/flask-app/venv/lib/python3.8/site-packages/click/exceptions.py new file mode 100644 index 000000000..9e20b3eb5 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click/exceptions.py @@ -0,0 +1,287 @@ +import os +import typing as t +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import get_text_stderr +from .utils import echo + +if t.TYPE_CHECKING: + from .core import Context + from .core import Parameter + + +def _join_param_hints( + param_hint: t.Optional[t.Union[t.Sequence[str], str]] +) -> t.Optional[str]: + if param_hint is not None and not isinstance(param_hint, str): + return " / ".join(repr(x) for x in param_hint) + + return param_hint + + +class ClickException(Exception): + """An exception that Click can handle and show to the user.""" + + #: The exit code for this exception. + exit_code = 1 + + def __init__(self, message: str) -> None: + super().__init__(message) + self.message = message + + def format_message(self) -> str: + return self.message + + def __str__(self) -> str: + return self.message + + def show(self, file: t.Optional[t.IO] = None) -> None: + if file is None: + file = get_text_stderr() + + echo(_("Error: {message}").format(message=self.format_message()), file=file) + + +class UsageError(ClickException): + """An internal exception that signals a usage error. This typically + aborts any further handling. + + :param message: the error message to display. + :param ctx: optionally the context that caused this error. Click will + fill in the context automatically in some situations. + """ + + exit_code = 2 + + def __init__(self, message: str, ctx: t.Optional["Context"] = None) -> None: + super().__init__(message) + self.ctx = ctx + self.cmd = self.ctx.command if self.ctx else None + + def show(self, file: t.Optional[t.IO] = None) -> None: + if file is None: + file = get_text_stderr() + color = None + hint = "" + if ( + self.ctx is not None + and self.ctx.command.get_help_option(self.ctx) is not None + ): + hint = _("Try '{command} {option}' for help.").format( + command=self.ctx.command_path, option=self.ctx.help_option_names[0] + ) + hint = f"{hint}\n" + if self.ctx is not None: + color = self.ctx.color + echo(f"{self.ctx.get_usage()}\n{hint}", file=file, color=color) + echo( + _("Error: {message}").format(message=self.format_message()), + file=file, + color=color, + ) + + +class BadParameter(UsageError): + """An exception that formats out a standardized error message for a + bad parameter. This is useful when thrown from a callback or type as + Click will attach contextual information to it (for instance, which + parameter it is). + + .. versionadded:: 2.0 + + :param param: the parameter object that caused this error. This can + be left out, and Click will attach this info itself + if possible. + :param param_hint: a string that shows up as parameter name. This + can be used as alternative to `param` in cases + where custom validation should happen. If it is + a string it's used as such, if it's a list then + each item is quoted and separated. + """ + + def __init__( + self, + message: str, + ctx: t.Optional["Context"] = None, + param: t.Optional["Parameter"] = None, + param_hint: t.Optional[str] = None, + ) -> None: + super().__init__(message, ctx) + self.param = param + self.param_hint = param_hint + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) # type: ignore + else: + return _("Invalid value: {message}").format(message=self.message) + + return _("Invalid value for {param_hint}: {message}").format( + param_hint=_join_param_hints(param_hint), message=self.message + ) + + +class MissingParameter(BadParameter): + """Raised if click required an option or argument but it was not + provided when invoking the script. + + .. versionadded:: 4.0 + + :param param_type: a string that indicates the type of the parameter. + The default is to inherit the parameter type from + the given `param`. Valid values are ``'parameter'``, + ``'option'`` or ``'argument'``. + """ + + def __init__( + self, + message: t.Optional[str] = None, + ctx: t.Optional["Context"] = None, + param: t.Optional["Parameter"] = None, + param_hint: t.Optional[str] = None, + param_type: t.Optional[str] = None, + ) -> None: + super().__init__(message or "", ctx, param, param_hint) + self.param_type = param_type + + def format_message(self) -> str: + if self.param_hint is not None: + param_hint: t.Optional[str] = self.param_hint + elif self.param is not None: + param_hint = self.param.get_error_hint(self.ctx) # type: ignore + else: + param_hint = None + + param_hint = _join_param_hints(param_hint) + param_hint = f" {param_hint}" if param_hint else "" + + param_type = self.param_type + if param_type is None and self.param is not None: + param_type = self.param.param_type_name + + msg = self.message + if self.param is not None: + msg_extra = self.param.type.get_missing_message(self.param) + if msg_extra: + if msg: + msg += f". {msg_extra}" + else: + msg = msg_extra + + msg = f" {msg}" if msg else "" + + # Translate param_type for known types. + if param_type == "argument": + missing = _("Missing argument") + elif param_type == "option": + missing = _("Missing option") + elif param_type == "parameter": + missing = _("Missing parameter") + else: + missing = _("Missing {param_type}").format(param_type=param_type) + + return f"{missing}{param_hint}.{msg}" + + def __str__(self) -> str: + if not self.message: + param_name = self.param.name if self.param else None + return _("Missing parameter: {param_name}").format(param_name=param_name) + else: + return self.message + + +class NoSuchOption(UsageError): + """Raised if click attempted to handle an option that does not + exist. + + .. versionadded:: 4.0 + """ + + def __init__( + self, + option_name: str, + message: t.Optional[str] = None, + possibilities: t.Optional[t.Sequence[str]] = None, + ctx: t.Optional["Context"] = None, + ) -> None: + if message is None: + message = _("No such option: {name}").format(name=option_name) + + super().__init__(message, ctx) + self.option_name = option_name + self.possibilities = possibilities + + def format_message(self) -> str: + if not self.possibilities: + return self.message + + possibility_str = ", ".join(sorted(self.possibilities)) + suggest = ngettext( + "Did you mean {possibility}?", + "(Possible options: {possibilities})", + len(self.possibilities), + ).format(possibility=possibility_str, possibilities=possibility_str) + return f"{self.message} {suggest}" + + +class BadOptionUsage(UsageError): + """Raised if an option is generally supplied but the use of the option + was incorrect. This is for instance raised if the number of arguments + for an option is not correct. + + .. versionadded:: 4.0 + + :param option_name: the name of the option being used incorrectly. + """ + + def __init__( + self, option_name: str, message: str, ctx: t.Optional["Context"] = None + ) -> None: + super().__init__(message, ctx) + self.option_name = option_name + + +class BadArgumentUsage(UsageError): + """Raised if an argument is generally supplied but the use of the argument + was incorrect. This is for instance raised if the number of values + for an argument is not correct. + + .. versionadded:: 6.0 + """ + + +class FileError(ClickException): + """Raised if a file cannot be opened.""" + + def __init__(self, filename: str, hint: t.Optional[str] = None) -> None: + if hint is None: + hint = _("unknown error") + + super().__init__(hint) + self.ui_filename = os.fsdecode(filename) + self.filename = filename + + def format_message(self) -> str: + return _("Could not open file {filename!r}: {message}").format( + filename=self.ui_filename, message=self.message + ) + + +class Abort(RuntimeError): + """An internal signalling exception that signals Click to abort.""" + + +class Exit(RuntimeError): + """An exception that indicates that the application should exit with some + status code. + + :param code: the status code to exit with. + """ + + __slots__ = ("exit_code",) + + def __init__(self, code: int = 0) -> None: + self.exit_code = code diff --git a/flask-app/venv/lib/python3.8/site-packages/click/formatting.py b/flask-app/venv/lib/python3.8/site-packages/click/formatting.py new file mode 100644 index 000000000..ddd2a2f82 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click/formatting.py @@ -0,0 +1,301 @@ +import typing as t +from contextlib import contextmanager +from gettext import gettext as _ + +from ._compat import term_len +from .parser import split_opt + +# Can force a width. This is used by the test system +FORCED_WIDTH: t.Optional[int] = None + + +def measure_table(rows: t.Iterable[t.Tuple[str, str]]) -> t.Tuple[int, ...]: + widths: t.Dict[int, int] = {} + + for row in rows: + for idx, col in enumerate(row): + widths[idx] = max(widths.get(idx, 0), term_len(col)) + + return tuple(y for x, y in sorted(widths.items())) + + +def iter_rows( + rows: t.Iterable[t.Tuple[str, str]], col_count: int +) -> t.Iterator[t.Tuple[str, ...]]: + for row in rows: + yield row + ("",) * (col_count - len(row)) + + +def wrap_text( + text: str, + width: int = 78, + initial_indent: str = "", + subsequent_indent: str = "", + preserve_paragraphs: bool = False, +) -> str: + """A helper function that intelligently wraps text. By default, it + assumes that it operates on a single paragraph of text but if the + `preserve_paragraphs` parameter is provided it will intelligently + handle paragraphs (defined by two empty lines). + + If paragraphs are handled, a paragraph can be prefixed with an empty + line containing the ``\\b`` character (``\\x08``) to indicate that + no rewrapping should happen in that block. + + :param text: the text that should be rewrapped. + :param width: the maximum width for the text. + :param initial_indent: the initial indent that should be placed on the + first line as a string. + :param subsequent_indent: the indent string that should be placed on + each consecutive line. + :param preserve_paragraphs: if this flag is set then the wrapping will + intelligently handle paragraphs. + """ + from ._textwrap import TextWrapper + + text = text.expandtabs() + wrapper = TextWrapper( + width, + initial_indent=initial_indent, + subsequent_indent=subsequent_indent, + replace_whitespace=False, + ) + if not preserve_paragraphs: + return wrapper.fill(text) + + p: t.List[t.Tuple[int, bool, str]] = [] + buf: t.List[str] = [] + indent = None + + def _flush_par() -> None: + if not buf: + return + if buf[0].strip() == "\b": + p.append((indent or 0, True, "\n".join(buf[1:]))) + else: + p.append((indent or 0, False, " ".join(buf))) + del buf[:] + + for line in text.splitlines(): + if not line: + _flush_par() + indent = None + else: + if indent is None: + orig_len = term_len(line) + line = line.lstrip() + indent = orig_len - term_len(line) + buf.append(line) + _flush_par() + + rv = [] + for indent, raw, text in p: + with wrapper.extra_indent(" " * indent): + if raw: + rv.append(wrapper.indent_only(text)) + else: + rv.append(wrapper.fill(text)) + + return "\n\n".join(rv) + + +class HelpFormatter: + """This class helps with formatting text-based help pages. It's + usually just needed for very special internal cases, but it's also + exposed so that developers can write their own fancy outputs. + + At present, it always writes into memory. + + :param indent_increment: the additional increment for each level. + :param width: the width for the text. This defaults to the terminal + width clamped to a maximum of 78. + """ + + def __init__( + self, + indent_increment: int = 2, + width: t.Optional[int] = None, + max_width: t.Optional[int] = None, + ) -> None: + import shutil + + self.indent_increment = indent_increment + if max_width is None: + max_width = 80 + if width is None: + width = FORCED_WIDTH + if width is None: + width = max(min(shutil.get_terminal_size().columns, max_width) - 2, 50) + self.width = width + self.current_indent = 0 + self.buffer: t.List[str] = [] + + def write(self, string: str) -> None: + """Writes a unicode string into the internal buffer.""" + self.buffer.append(string) + + def indent(self) -> None: + """Increases the indentation.""" + self.current_indent += self.indent_increment + + def dedent(self) -> None: + """Decreases the indentation.""" + self.current_indent -= self.indent_increment + + def write_usage( + self, prog: str, args: str = "", prefix: t.Optional[str] = None + ) -> None: + """Writes a usage line into the buffer. + + :param prog: the program name. + :param args: whitespace separated list of arguments. + :param prefix: The prefix for the first line. Defaults to + ``"Usage: "``. + """ + if prefix is None: + prefix = f"{_('Usage:')} " + + usage_prefix = f"{prefix:>{self.current_indent}}{prog} " + text_width = self.width - self.current_indent + + if text_width >= (term_len(usage_prefix) + 20): + # The arguments will fit to the right of the prefix. + indent = " " * term_len(usage_prefix) + self.write( + wrap_text( + args, + text_width, + initial_indent=usage_prefix, + subsequent_indent=indent, + ) + ) + else: + # The prefix is too long, put the arguments on the next line. + self.write(usage_prefix) + self.write("\n") + indent = " " * (max(self.current_indent, term_len(prefix)) + 4) + self.write( + wrap_text( + args, text_width, initial_indent=indent, subsequent_indent=indent + ) + ) + + self.write("\n") + + def write_heading(self, heading: str) -> None: + """Writes a heading into the buffer.""" + self.write(f"{'':>{self.current_indent}}{heading}:\n") + + def write_paragraph(self) -> None: + """Writes a paragraph into the buffer.""" + if self.buffer: + self.write("\n") + + def write_text(self, text: str) -> None: + """Writes re-indented text into the buffer. This rewraps and + preserves paragraphs. + """ + indent = " " * self.current_indent + self.write( + wrap_text( + text, + self.width, + initial_indent=indent, + subsequent_indent=indent, + preserve_paragraphs=True, + ) + ) + self.write("\n") + + def write_dl( + self, + rows: t.Sequence[t.Tuple[str, str]], + col_max: int = 30, + col_spacing: int = 2, + ) -> None: + """Writes a definition list into the buffer. This is how options + and commands are usually formatted. + + :param rows: a list of two item tuples for the terms and values. + :param col_max: the maximum width of the first column. + :param col_spacing: the number of spaces between the first and + second column. + """ + rows = list(rows) + widths = measure_table(rows) + if len(widths) != 2: + raise TypeError("Expected two columns for definition list") + + first_col = min(widths[0], col_max) + col_spacing + + for first, second in iter_rows(rows, len(widths)): + self.write(f"{'':>{self.current_indent}}{first}") + if not second: + self.write("\n") + continue + if term_len(first) <= first_col - col_spacing: + self.write(" " * (first_col - term_len(first))) + else: + self.write("\n") + self.write(" " * (first_col + self.current_indent)) + + text_width = max(self.width - first_col - 2, 10) + wrapped_text = wrap_text(second, text_width, preserve_paragraphs=True) + lines = wrapped_text.splitlines() + + if lines: + self.write(f"{lines[0]}\n") + + for line in lines[1:]: + self.write(f"{'':>{first_col + self.current_indent}}{line}\n") + else: + self.write("\n") + + @contextmanager + def section(self, name: str) -> t.Iterator[None]: + """Helpful context manager that writes a paragraph, a heading, + and the indents. + + :param name: the section name that is written as heading. + """ + self.write_paragraph() + self.write_heading(name) + self.indent() + try: + yield + finally: + self.dedent() + + @contextmanager + def indentation(self) -> t.Iterator[None]: + """A context manager that increases the indentation.""" + self.indent() + try: + yield + finally: + self.dedent() + + def getvalue(self) -> str: + """Returns the buffer contents.""" + return "".join(self.buffer) + + +def join_options(options: t.Sequence[str]) -> t.Tuple[str, bool]: + """Given a list of option strings this joins them in the most appropriate + way and returns them in the form ``(formatted_string, + any_prefix_is_slash)`` where the second item in the tuple is a flag that + indicates if any of the option prefixes was a slash. + """ + rv = [] + any_prefix_is_slash = False + + for opt in options: + prefix = split_opt(opt)[0] + + if prefix == "/": + any_prefix_is_slash = True + + rv.append((len(prefix), opt)) + + rv.sort(key=lambda x: x[0]) + return ", ".join(x[1] for x in rv), any_prefix_is_slash diff --git a/flask-app/venv/lib/python3.8/site-packages/click/globals.py b/flask-app/venv/lib/python3.8/site-packages/click/globals.py new file mode 100644 index 000000000..a7b0c9317 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click/globals.py @@ -0,0 +1,69 @@ +import typing +import typing as t +from threading import local + +if t.TYPE_CHECKING: + import typing_extensions as te + from .core import Context + +_local = local() + + +@typing.overload +def get_current_context(silent: "te.Literal[False]" = False) -> "Context": + ... + + +@typing.overload +def get_current_context(silent: bool = ...) -> t.Optional["Context"]: + ... + + +def get_current_context(silent: bool = False) -> t.Optional["Context"]: + """Returns the current click context. This can be used as a way to + access the current context object from anywhere. This is a more implicit + alternative to the :func:`pass_context` decorator. This function is + primarily useful for helpers such as :func:`echo` which might be + interested in changing its behavior based on the current context. + + To push the current context, :meth:`Context.scope` can be used. + + .. versionadded:: 5.0 + + :param silent: if set to `True` the return value is `None` if no context + is available. The default behavior is to raise a + :exc:`RuntimeError`. + """ + try: + return t.cast("Context", _local.stack[-1]) + except (AttributeError, IndexError) as e: + if not silent: + raise RuntimeError("There is no active click context.") from e + + return None + + +def push_context(ctx: "Context") -> None: + """Pushes a new context to the current stack.""" + _local.__dict__.setdefault("stack", []).append(ctx) + + +def pop_context() -> None: + """Removes the top level from the stack.""" + _local.stack.pop() + + +def resolve_color_default(color: t.Optional[bool] = None) -> t.Optional[bool]: + """Internal helper to get the default value of the color flag. If a + value is passed it's returned unchanged, otherwise it's looked up from + the current context. + """ + if color is not None: + return color + + ctx = get_current_context(silent=True) + + if ctx is not None: + return ctx.color + + return None diff --git a/flask-app/venv/lib/python3.8/site-packages/click/parser.py b/flask-app/venv/lib/python3.8/site-packages/click/parser.py new file mode 100644 index 000000000..2d5a2ed7b --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click/parser.py @@ -0,0 +1,529 @@ +""" +This module started out as largely a copy paste from the stdlib's +optparse module with the features removed that we do not need from +optparse because we implement them in Click on a higher level (for +instance type handling, help formatting and a lot more). + +The plan is to remove more and more from here over time. + +The reason this is a different module and not optparse from the stdlib +is that there are differences in 2.x and 3.x about the error messages +generated and optparse in the stdlib uses gettext for no good reason +and might cause us issues. + +Click uses parts of optparse written by Gregory P. Ward and maintained +by the Python Software Foundation. This is limited to code in parser.py. + +Copyright 2001-2006 Gregory P. Ward. All rights reserved. +Copyright 2002-2006 Python Software Foundation. All rights reserved. +""" +# This code uses parts of optparse written by Gregory P. Ward and +# maintained by the Python Software Foundation. +# Copyright 2001-2006 Gregory P. Ward +# Copyright 2002-2006 Python Software Foundation +import typing as t +from collections import deque +from gettext import gettext as _ +from gettext import ngettext + +from .exceptions import BadArgumentUsage +from .exceptions import BadOptionUsage +from .exceptions import NoSuchOption +from .exceptions import UsageError + +if t.TYPE_CHECKING: + import typing_extensions as te + from .core import Argument as CoreArgument + from .core import Context + from .core import Option as CoreOption + from .core import Parameter as CoreParameter + +V = t.TypeVar("V") + +# Sentinel value that indicates an option was passed as a flag without a +# value but is not a flag option. Option.consume_value uses this to +# prompt or use the flag_value. +_flag_needs_value = object() + + +def _unpack_args( + args: t.Sequence[str], nargs_spec: t.Sequence[int] +) -> t.Tuple[t.Sequence[t.Union[str, t.Sequence[t.Optional[str]], None]], t.List[str]]: + """Given an iterable of arguments and an iterable of nargs specifications, + it returns a tuple with all the unpacked arguments at the first index + and all remaining arguments as the second. + + The nargs specification is the number of arguments that should be consumed + or `-1` to indicate that this position should eat up all the remainders. + + Missing items are filled with `None`. + """ + args = deque(args) + nargs_spec = deque(nargs_spec) + rv: t.List[t.Union[str, t.Tuple[t.Optional[str], ...], None]] = [] + spos: t.Optional[int] = None + + def _fetch(c: "te.Deque[V]") -> t.Optional[V]: + try: + if spos is None: + return c.popleft() + else: + return c.pop() + except IndexError: + return None + + while nargs_spec: + nargs = _fetch(nargs_spec) + + if nargs is None: + continue + + if nargs == 1: + rv.append(_fetch(args)) + elif nargs > 1: + x = [_fetch(args) for _ in range(nargs)] + + # If we're reversed, we're pulling in the arguments in reverse, + # so we need to turn them around. + if spos is not None: + x.reverse() + + rv.append(tuple(x)) + elif nargs < 0: + if spos is not None: + raise TypeError("Cannot have two nargs < 0") + + spos = len(rv) + rv.append(None) + + # spos is the position of the wildcard (star). If it's not `None`, + # we fill it with the remainder. + if spos is not None: + rv[spos] = tuple(args) + args = [] + rv[spos + 1 :] = reversed(rv[spos + 1 :]) + + return tuple(rv), list(args) + + +def split_opt(opt: str) -> t.Tuple[str, str]: + first = opt[:1] + if first.isalnum(): + return "", opt + if opt[1:2] == first: + return opt[:2], opt[2:] + return first, opt[1:] + + +def normalize_opt(opt: str, ctx: t.Optional["Context"]) -> str: + if ctx is None or ctx.token_normalize_func is None: + return opt + prefix, opt = split_opt(opt) + return f"{prefix}{ctx.token_normalize_func(opt)}" + + +def split_arg_string(string: str) -> t.List[str]: + """Split an argument string as with :func:`shlex.split`, but don't + fail if the string is incomplete. Ignores a missing closing quote or + incomplete escape sequence and uses the partial token as-is. + + .. code-block:: python + + split_arg_string("example 'my file") + ["example", "my file"] + + split_arg_string("example my\\") + ["example", "my"] + + :param string: String to split. + """ + import shlex + + lex = shlex.shlex(string, posix=True) + lex.whitespace_split = True + lex.commenters = "" + out = [] + + try: + for token in lex: + out.append(token) + except ValueError: + # Raised when end-of-string is reached in an invalid state. Use + # the partial token as-is. The quote or escape character is in + # lex.state, not lex.token. + out.append(lex.token) + + return out + + +class Option: + def __init__( + self, + obj: "CoreOption", + opts: t.Sequence[str], + dest: t.Optional[str], + action: t.Optional[str] = None, + nargs: int = 1, + const: t.Optional[t.Any] = None, + ): + self._short_opts = [] + self._long_opts = [] + self.prefixes = set() + + for opt in opts: + prefix, value = split_opt(opt) + if not prefix: + raise ValueError(f"Invalid start character for option ({opt})") + self.prefixes.add(prefix[0]) + if len(prefix) == 1 and len(value) == 1: + self._short_opts.append(opt) + else: + self._long_opts.append(opt) + self.prefixes.add(prefix) + + if action is None: + action = "store" + + self.dest = dest + self.action = action + self.nargs = nargs + self.const = const + self.obj = obj + + @property + def takes_value(self) -> bool: + return self.action in ("store", "append") + + def process(self, value: str, state: "ParsingState") -> None: + if self.action == "store": + state.opts[self.dest] = value # type: ignore + elif self.action == "store_const": + state.opts[self.dest] = self.const # type: ignore + elif self.action == "append": + state.opts.setdefault(self.dest, []).append(value) # type: ignore + elif self.action == "append_const": + state.opts.setdefault(self.dest, []).append(self.const) # type: ignore + elif self.action == "count": + state.opts[self.dest] = state.opts.get(self.dest, 0) + 1 # type: ignore + else: + raise ValueError(f"unknown action '{self.action}'") + state.order.append(self.obj) + + +class Argument: + def __init__(self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1): + self.dest = dest + self.nargs = nargs + self.obj = obj + + def process( + self, + value: t.Union[t.Optional[str], t.Sequence[t.Optional[str]]], + state: "ParsingState", + ) -> None: + if self.nargs > 1: + assert value is not None + holes = sum(1 for x in value if x is None) + if holes == len(value): + value = None + elif holes != 0: + raise BadArgumentUsage( + _("Argument {name!r} takes {nargs} values.").format( + name=self.dest, nargs=self.nargs + ) + ) + + if self.nargs == -1 and self.obj.envvar is not None and value == (): + # Replace empty tuple with None so that a value from the + # environment may be tried. + value = None + + state.opts[self.dest] = value # type: ignore + state.order.append(self.obj) + + +class ParsingState: + def __init__(self, rargs: t.List[str]) -> None: + self.opts: t.Dict[str, t.Any] = {} + self.largs: t.List[str] = [] + self.rargs = rargs + self.order: t.List["CoreParameter"] = [] + + +class OptionParser: + """The option parser is an internal class that is ultimately used to + parse options and arguments. It's modelled after optparse and brings + a similar but vastly simplified API. It should generally not be used + directly as the high level Click classes wrap it for you. + + It's not nearly as extensible as optparse or argparse as it does not + implement features that are implemented on a higher level (such as + types or defaults). + + :param ctx: optionally the :class:`~click.Context` where this parser + should go with. + """ + + def __init__(self, ctx: t.Optional["Context"] = None) -> None: + #: The :class:`~click.Context` for this parser. This might be + #: `None` for some advanced use cases. + self.ctx = ctx + #: This controls how the parser deals with interspersed arguments. + #: If this is set to `False`, the parser will stop on the first + #: non-option. Click uses this to implement nested subcommands + #: safely. + self.allow_interspersed_args = True + #: This tells the parser how to deal with unknown options. By + #: default it will error out (which is sensible), but there is a + #: second mode where it will ignore it and continue processing + #: after shifting all the unknown options into the resulting args. + self.ignore_unknown_options = False + + if ctx is not None: + self.allow_interspersed_args = ctx.allow_interspersed_args + self.ignore_unknown_options = ctx.ignore_unknown_options + + self._short_opt: t.Dict[str, Option] = {} + self._long_opt: t.Dict[str, Option] = {} + self._opt_prefixes = {"-", "--"} + self._args: t.List[Argument] = [] + + def add_option( + self, + obj: "CoreOption", + opts: t.Sequence[str], + dest: t.Optional[str], + action: t.Optional[str] = None, + nargs: int = 1, + const: t.Optional[t.Any] = None, + ) -> None: + """Adds a new option named `dest` to the parser. The destination + is not inferred (unlike with optparse) and needs to be explicitly + provided. Action can be any of ``store``, ``store_const``, + ``append``, ``append_const`` or ``count``. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + opts = [normalize_opt(opt, self.ctx) for opt in opts] + option = Option(obj, opts, dest, action=action, nargs=nargs, const=const) + self._opt_prefixes.update(option.prefixes) + for opt in option._short_opts: + self._short_opt[opt] = option + for opt in option._long_opts: + self._long_opt[opt] = option + + def add_argument( + self, obj: "CoreArgument", dest: t.Optional[str], nargs: int = 1 + ) -> None: + """Adds a positional argument named `dest` to the parser. + + The `obj` can be used to identify the option in the order list + that is returned from the parser. + """ + self._args.append(Argument(obj, dest=dest, nargs=nargs)) + + def parse_args( + self, args: t.List[str] + ) -> t.Tuple[t.Dict[str, t.Any], t.List[str], t.List["CoreParameter"]]: + """Parses positional arguments and returns ``(values, args, order)`` + for the parsed options and arguments as well as the leftover + arguments if there are any. The order is a list of objects as they + appear on the command line. If arguments appear multiple times they + will be memorized multiple times as well. + """ + state = ParsingState(args) + try: + self._process_args_for_options(state) + self._process_args_for_args(state) + except UsageError: + if self.ctx is None or not self.ctx.resilient_parsing: + raise + return state.opts, state.largs, state.order + + def _process_args_for_args(self, state: ParsingState) -> None: + pargs, args = _unpack_args( + state.largs + state.rargs, [x.nargs for x in self._args] + ) + + for idx, arg in enumerate(self._args): + arg.process(pargs[idx], state) + + state.largs = args + state.rargs = [] + + def _process_args_for_options(self, state: ParsingState) -> None: + while state.rargs: + arg = state.rargs.pop(0) + arglen = len(arg) + # Double dashes always handled explicitly regardless of what + # prefixes are valid. + if arg == "--": + return + elif arg[:1] in self._opt_prefixes and arglen > 1: + self._process_opts(arg, state) + elif self.allow_interspersed_args: + state.largs.append(arg) + else: + state.rargs.insert(0, arg) + return + + # Say this is the original argument list: + # [arg0, arg1, ..., arg(i-1), arg(i), arg(i+1), ..., arg(N-1)] + # ^ + # (we are about to process arg(i)). + # + # Then rargs is [arg(i), ..., arg(N-1)] and largs is a *subset* of + # [arg0, ..., arg(i-1)] (any options and their arguments will have + # been removed from largs). + # + # The while loop will usually consume 1 or more arguments per pass. + # If it consumes 1 (eg. arg is an option that takes no arguments), + # then after _process_arg() is done the situation is: + # + # largs = subset of [arg0, ..., arg(i)] + # rargs = [arg(i+1), ..., arg(N-1)] + # + # If allow_interspersed_args is false, largs will always be + # *empty* -- still a subset of [arg0, ..., arg(i-1)], but + # not a very interesting subset! + + def _match_long_opt( + self, opt: str, explicit_value: t.Optional[str], state: ParsingState + ) -> None: + if opt not in self._long_opt: + from difflib import get_close_matches + + possibilities = get_close_matches(opt, self._long_opt) + raise NoSuchOption(opt, possibilities=possibilities, ctx=self.ctx) + + option = self._long_opt[opt] + if option.takes_value: + # At this point it's safe to modify rargs by injecting the + # explicit value, because no exception is raised in this + # branch. This means that the inserted value will be fully + # consumed. + if explicit_value is not None: + state.rargs.insert(0, explicit_value) + + value = self._get_value_from_state(opt, option, state) + + elif explicit_value is not None: + raise BadOptionUsage( + opt, _("Option {name!r} does not take a value.").format(name=opt) + ) + + else: + value = None + + option.process(value, state) + + def _match_short_opt(self, arg: str, state: ParsingState) -> None: + stop = False + i = 1 + prefix = arg[0] + unknown_options = [] + + for ch in arg[1:]: + opt = normalize_opt(f"{prefix}{ch}", self.ctx) + option = self._short_opt.get(opt) + i += 1 + + if not option: + if self.ignore_unknown_options: + unknown_options.append(ch) + continue + raise NoSuchOption(opt, ctx=self.ctx) + if option.takes_value: + # Any characters left in arg? Pretend they're the + # next arg, and stop consuming characters of arg. + if i < len(arg): + state.rargs.insert(0, arg[i:]) + stop = True + + value = self._get_value_from_state(opt, option, state) + + else: + value = None + + option.process(value, state) + + if stop: + break + + # If we got any unknown options we re-combinate the string of the + # remaining options and re-attach the prefix, then report that + # to the state as new larg. This way there is basic combinatorics + # that can be achieved while still ignoring unknown arguments. + if self.ignore_unknown_options and unknown_options: + state.largs.append(f"{prefix}{''.join(unknown_options)}") + + def _get_value_from_state( + self, option_name: str, option: Option, state: ParsingState + ) -> t.Any: + nargs = option.nargs + + if len(state.rargs) < nargs: + if option.obj._flag_needs_value: + # Option allows omitting the value. + value = _flag_needs_value + else: + raise BadOptionUsage( + option_name, + ngettext( + "Option {name!r} requires an argument.", + "Option {name!r} requires {nargs} arguments.", + nargs, + ).format(name=option_name, nargs=nargs), + ) + elif nargs == 1: + next_rarg = state.rargs[0] + + if ( + option.obj._flag_needs_value + and isinstance(next_rarg, str) + and next_rarg[:1] in self._opt_prefixes + and len(next_rarg) > 1 + ): + # The next arg looks like the start of an option, don't + # use it as the value if omitting the value is allowed. + value = _flag_needs_value + else: + value = state.rargs.pop(0) + else: + value = tuple(state.rargs[:nargs]) + del state.rargs[:nargs] + + return value + + def _process_opts(self, arg: str, state: ParsingState) -> None: + explicit_value = None + # Long option handling happens in two parts. The first part is + # supporting explicitly attached values. In any case, we will try + # to long match the option first. + if "=" in arg: + long_opt, explicit_value = arg.split("=", 1) + else: + long_opt = arg + norm_long_opt = normalize_opt(long_opt, self.ctx) + + # At this point we will match the (assumed) long option through + # the long option matching code. Note that this allows options + # like "-foo" to be matched as long options. + try: + self._match_long_opt(norm_long_opt, explicit_value, state) + except NoSuchOption: + # At this point the long option matching failed, and we need + # to try with short options. However there is a special rule + # which says, that if we have a two character options prefix + # (applies to "--foo" for instance), we do not dispatch to the + # short option code and will instead raise the no option + # error. + if arg[:2] not in self._opt_prefixes: + self._match_short_opt(arg, state) + return + + if not self.ignore_unknown_options: + raise + + state.largs.append(arg) diff --git a/flask-app/venv/lib/python3.8/site-packages/click/py.typed b/flask-app/venv/lib/python3.8/site-packages/click/py.typed new file mode 100644 index 000000000..e69de29bb diff --git a/flask-app/venv/lib/python3.8/site-packages/click/shell_completion.py b/flask-app/venv/lib/python3.8/site-packages/click/shell_completion.py new file mode 100644 index 000000000..cad080da6 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click/shell_completion.py @@ -0,0 +1,581 @@ +import os +import re +import typing as t +from gettext import gettext as _ + +from .core import Argument +from .core import BaseCommand +from .core import Context +from .core import MultiCommand +from .core import Option +from .core import Parameter +from .core import ParameterSource +from .parser import split_arg_string +from .utils import echo + + +def shell_complete( + cli: BaseCommand, + ctx_args: t.Dict[str, t.Any], + prog_name: str, + complete_var: str, + instruction: str, +) -> int: + """Perform shell completion for the given CLI program. + + :param cli: Command being called. + :param ctx_args: Extra arguments to pass to + ``cli.make_context``. + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. + :param instruction: Value of ``complete_var`` with the completion + instruction and shell, in the form ``instruction_shell``. + :return: Status code to exit with. + """ + shell, _, instruction = instruction.partition("_") + comp_cls = get_completion_class(shell) + + if comp_cls is None: + return 1 + + comp = comp_cls(cli, ctx_args, prog_name, complete_var) + + if instruction == "source": + echo(comp.source()) + return 0 + + if instruction == "complete": + echo(comp.complete()) + return 0 + + return 1 + + +class CompletionItem: + """Represents a completion value and metadata about the value. The + default metadata is ``type`` to indicate special shell handling, + and ``help`` if a shell supports showing a help string next to the + value. + + Arbitrary parameters can be passed when creating the object, and + accessed using ``item.attr``. If an attribute wasn't passed, + accessing it returns ``None``. + + :param value: The completion suggestion. + :param type: Tells the shell script to provide special completion + support for the type. Click uses ``"dir"`` and ``"file"``. + :param help: String shown next to the value if supported. + :param kwargs: Arbitrary metadata. The built-in implementations + don't use this, but custom type completions paired with custom + shell support could use it. + """ + + __slots__ = ("value", "type", "help", "_info") + + def __init__( + self, + value: t.Any, + type: str = "plain", + help: t.Optional[str] = None, + **kwargs: t.Any, + ) -> None: + self.value = value + self.type = type + self.help = help + self._info = kwargs + + def __getattr__(self, name: str) -> t.Any: + return self._info.get(name) + + +# Only Bash >= 4.4 has the nosort option. +_SOURCE_BASH = """\ +%(complete_func)s() { + local IFS=$'\\n' + local response + + response=$(env COMP_WORDS="${COMP_WORDS[*]}" COMP_CWORD=$COMP_CWORD \ +%(complete_var)s=bash_complete $1) + + for completion in $response; do + IFS=',' read type value <<< "$completion" + + if [[ $type == 'dir' ]]; then + COMREPLY=() + compopt -o dirnames + elif [[ $type == 'file' ]]; then + COMREPLY=() + compopt -o default + elif [[ $type == 'plain' ]]; then + COMPREPLY+=($value) + fi + done + + return 0 +} + +%(complete_func)s_setup() { + complete -o nosort -F %(complete_func)s %(prog_name)s +} + +%(complete_func)s_setup; +""" + +_SOURCE_ZSH = """\ +#compdef %(prog_name)s + +%(complete_func)s() { + local -a completions + local -a completions_with_descriptions + local -a response + (( ! $+commands[%(prog_name)s] )) && return 1 + + response=("${(@f)$(env COMP_WORDS="${words[*]}" COMP_CWORD=$((CURRENT-1)) \ +%(complete_var)s=zsh_complete %(prog_name)s)}") + + for type key descr in ${response}; do + if [[ "$type" == "plain" ]]; then + if [[ "$descr" == "_" ]]; then + completions+=("$key") + else + completions_with_descriptions+=("$key":"$descr") + fi + elif [[ "$type" == "dir" ]]; then + _path_files -/ + elif [[ "$type" == "file" ]]; then + _path_files -f + fi + done + + if [ -n "$completions_with_descriptions" ]; then + _describe -V unsorted completions_with_descriptions -U + fi + + if [ -n "$completions" ]; then + compadd -U -V unsorted -a completions + fi +} + +compdef %(complete_func)s %(prog_name)s; +""" + +_SOURCE_FISH = """\ +function %(complete_func)s; + set -l response; + + for value in (env %(complete_var)s=fish_complete COMP_WORDS=(commandline -cp) \ +COMP_CWORD=(commandline -t) %(prog_name)s); + set response $response $value; + end; + + for completion in $response; + set -l metadata (string split "," $completion); + + if test $metadata[1] = "dir"; + __fish_complete_directories $metadata[2]; + else if test $metadata[1] = "file"; + __fish_complete_path $metadata[2]; + else if test $metadata[1] = "plain"; + echo $metadata[2]; + end; + end; +end; + +complete --no-files --command %(prog_name)s --arguments \ +"(%(complete_func)s)"; +""" + + +class ShellComplete: + """Base class for providing shell completion support. A subclass for + a given shell will override attributes and methods to implement the + completion instructions (``source`` and ``complete``). + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param complete_var: Name of the environment variable that holds + the completion instruction. + + .. versionadded:: 8.0 + """ + + name: t.ClassVar[str] + """Name to register the shell as with :func:`add_completion_class`. + This is used in completion instructions (``{name}_source`` and + ``{name}_complete``). + """ + + source_template: t.ClassVar[str] + """Completion script template formatted by :meth:`source`. This must + be provided by subclasses. + """ + + def __init__( + self, + cli: BaseCommand, + ctx_args: t.Dict[str, t.Any], + prog_name: str, + complete_var: str, + ) -> None: + self.cli = cli + self.ctx_args = ctx_args + self.prog_name = prog_name + self.complete_var = complete_var + + @property + def func_name(self) -> str: + """The name of the shell function defined by the completion + script. + """ + safe_name = re.sub(r"\W*", "", self.prog_name.replace("-", "_"), re.ASCII) + return f"_{safe_name}_completion" + + def source_vars(self) -> t.Dict[str, t.Any]: + """Vars for formatting :attr:`source_template`. + + By default this provides ``complete_func``, ``complete_var``, + and ``prog_name``. + """ + return { + "complete_func": self.func_name, + "complete_var": self.complete_var, + "prog_name": self.prog_name, + } + + def source(self) -> str: + """Produce the shell script that defines the completion + function. By default this ``%``-style formats + :attr:`source_template` with the dict returned by + :meth:`source_vars`. + """ + return self.source_template % self.source_vars() + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + """Use the env vars defined by the shell script to return a + tuple of ``args, incomplete``. This must be implemented by + subclasses. + """ + raise NotImplementedError + + def get_completions( + self, args: t.List[str], incomplete: str + ) -> t.List[CompletionItem]: + """Determine the context and last complete command or parameter + from the complete args. Call that object's ``shell_complete`` + method to get the completions for the incomplete value. + + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + ctx = _resolve_context(self.cli, self.ctx_args, self.prog_name, args) + obj, incomplete = _resolve_incomplete(ctx, args, incomplete) + return obj.shell_complete(ctx, incomplete) + + def format_completion(self, item: CompletionItem) -> str: + """Format a completion item into the form recognized by the + shell script. This must be implemented by subclasses. + + :param item: Completion item to format. + """ + raise NotImplementedError + + def complete(self) -> str: + """Produce the completion data to send back to the shell. + + By default this calls :meth:`get_completion_args`, gets the + completions, then calls :meth:`format_completion` for each + completion. + """ + args, incomplete = self.get_completion_args() + completions = self.get_completions(args, incomplete) + out = [self.format_completion(item) for item in completions] + return "\n".join(out) + + +class BashComplete(ShellComplete): + """Shell completion for Bash.""" + + name = "bash" + source_template = _SOURCE_BASH + + def _check_version(self) -> None: + import subprocess + + output = subprocess.run( + ["bash", "-c", "echo ${BASH_VERSION}"], stdout=subprocess.PIPE + ) + match = re.search(r"^(\d+)\.(\d+)\.\d+", output.stdout.decode()) + + if match is not None: + major, minor = match.groups() + + if major < "4" or major == "4" and minor < "4": + raise RuntimeError( + _( + "Shell completion is not supported for Bash" + " versions older than 4.4." + ) + ) + else: + raise RuntimeError( + _("Couldn't detect Bash version, shell completion is not supported.") + ) + + def source(self) -> str: + self._check_version() + return super().source() + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + return f"{item.type},{item.value}" + + +class ZshComplete(ShellComplete): + """Shell completion for Zsh.""" + + name = "zsh" + source_template = _SOURCE_ZSH + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + cword = int(os.environ["COMP_CWORD"]) + args = cwords[1:cword] + + try: + incomplete = cwords[cword] + except IndexError: + incomplete = "" + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + return f"{item.type}\n{item.value}\n{item.help if item.help else '_'}" + + +class FishComplete(ShellComplete): + """Shell completion for Fish.""" + + name = "fish" + source_template = _SOURCE_FISH + + def get_completion_args(self) -> t.Tuple[t.List[str], str]: + cwords = split_arg_string(os.environ["COMP_WORDS"]) + incomplete = os.environ["COMP_CWORD"] + args = cwords[1:] + + # Fish stores the partial word in both COMP_WORDS and + # COMP_CWORD, remove it from complete args. + if incomplete and args and args[-1] == incomplete: + args.pop() + + return args, incomplete + + def format_completion(self, item: CompletionItem) -> str: + if item.help: + return f"{item.type},{item.value}\t{item.help}" + + return f"{item.type},{item.value}" + + +_available_shells: t.Dict[str, t.Type[ShellComplete]] = { + "bash": BashComplete, + "fish": FishComplete, + "zsh": ZshComplete, +} + + +def add_completion_class( + cls: t.Type[ShellComplete], name: t.Optional[str] = None +) -> None: + """Register a :class:`ShellComplete` subclass under the given name. + The name will be provided by the completion instruction environment + variable during completion. + + :param cls: The completion class that will handle completion for the + shell. + :param name: Name to register the class under. Defaults to the + class's ``name`` attribute. + """ + if name is None: + name = cls.name + + _available_shells[name] = cls + + +def get_completion_class(shell: str) -> t.Optional[t.Type[ShellComplete]]: + """Look up a registered :class:`ShellComplete` subclass by the name + provided by the completion instruction environment variable. If the + name isn't registered, returns ``None``. + + :param shell: Name the class is registered under. + """ + return _available_shells.get(shell) + + +def _is_incomplete_argument(ctx: Context, param: Parameter) -> bool: + """Determine if the given parameter is an argument that can still + accept values. + + :param ctx: Invocation context for the command represented by the + parsed complete args. + :param param: Argument object being checked. + """ + if not isinstance(param, Argument): + return False + + assert param.name is not None + value = ctx.params[param.name] + return ( + param.nargs == -1 + or ctx.get_parameter_source(param.name) is not ParameterSource.COMMANDLINE + or ( + param.nargs > 1 + and isinstance(value, (tuple, list)) + and len(value) < param.nargs + ) + ) + + +def _start_of_option(value: str) -> bool: + """Check if the value looks like the start of an option.""" + if not value: + return False + + c = value[0] + # Allow "/" since that starts a path. + return not c.isalnum() and c != "/" + + +def _is_incomplete_option(args: t.List[str], param: Parameter) -> bool: + """Determine if the given parameter is an option that needs a value. + + :param args: List of complete args before the incomplete value. + :param param: Option object being checked. + """ + if not isinstance(param, Option): + return False + + if param.is_flag: + return False + + last_option = None + + for index, arg in enumerate(reversed(args)): + if index + 1 > param.nargs: + break + + if _start_of_option(arg): + last_option = arg + + return last_option is not None and last_option in param.opts + + +def _resolve_context( + cli: BaseCommand, ctx_args: t.Dict[str, t.Any], prog_name: str, args: t.List[str] +) -> Context: + """Produce the context hierarchy starting with the command and + traversing the complete arguments. This only follows the commands, + it doesn't trigger input prompts or callbacks. + + :param cli: Command being called. + :param prog_name: Name of the executable in the shell. + :param args: List of complete args before the incomplete value. + """ + ctx_args["resilient_parsing"] = True + ctx = cli.make_context(prog_name, args.copy(), **ctx_args) + args = ctx.protected_args + ctx.args + + while args: + command = ctx.command + + if isinstance(command, MultiCommand): + if not command.chain: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + ctx = cmd.make_context(name, args, parent=ctx, resilient_parsing=True) + args = ctx.protected_args + ctx.args + else: + while args: + name, cmd, args = command.resolve_command(ctx, args) + + if cmd is None: + return ctx + + sub_ctx = cmd.make_context( + name, + args, + parent=ctx, + allow_extra_args=True, + allow_interspersed_args=False, + resilient_parsing=True, + ) + args = sub_ctx.args + + ctx = sub_ctx + args = [*sub_ctx.protected_args, *sub_ctx.args] + else: + break + + return ctx + + +def _resolve_incomplete( + ctx: Context, args: t.List[str], incomplete: str +) -> t.Tuple[t.Union[BaseCommand, Parameter], str]: + """Find the Click object that will handle the completion of the + incomplete value. Return the object and the incomplete value. + + :param ctx: Invocation context for the command represented by + the parsed complete args. + :param args: List of complete args before the incomplete value. + :param incomplete: Value being completed. May be empty. + """ + # Different shells treat an "=" between a long option name and + # value differently. Might keep the value joined, return the "=" + # as a separate item, or return the split name and value. Always + # split and discard the "=" to make completion easier. + if incomplete == "=": + incomplete = "" + elif "=" in incomplete and _start_of_option(incomplete): + name, _, incomplete = incomplete.partition("=") + args.append(name) + + # The "--" marker tells Click to stop treating values as options + # even if they start with the option character. If it hasn't been + # given and the incomplete arg looks like an option, the current + # command will provide option name completions. + if "--" not in args and _start_of_option(incomplete): + return ctx.command, incomplete + + params = ctx.command.get_params(ctx) + + # If the last complete arg is an option name with an incomplete + # value, the option will provide value completions. + for param in params: + if _is_incomplete_option(args, param): + return param, incomplete + + # It's not an option name or value. The first argument without a + # parsed value will provide value completions. + for param in params: + if _is_incomplete_argument(ctx, param): + return param, incomplete + + # There were no unparsed arguments, the command may be a group that + # will provide command name completions. + return ctx.command, incomplete diff --git a/flask-app/venv/lib/python3.8/site-packages/click/termui.py b/flask-app/venv/lib/python3.8/site-packages/click/termui.py new file mode 100644 index 000000000..cf8d5f132 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click/termui.py @@ -0,0 +1,809 @@ +import inspect +import io +import itertools +import os +import sys +import typing +import typing as t +from gettext import gettext as _ + +from ._compat import isatty +from ._compat import strip_ansi +from ._compat import WIN +from .exceptions import Abort +from .exceptions import UsageError +from .globals import resolve_color_default +from .types import Choice +from .types import convert_type +from .types import ParamType +from .utils import echo +from .utils import LazyFile + +if t.TYPE_CHECKING: + from ._termui_impl import ProgressBar + +V = t.TypeVar("V") + +# The prompt functions to use. The doc tools currently override these +# functions to customize how they work. +visible_prompt_func: t.Callable[[str], str] = input + +_ansi_colors = { + "black": 30, + "red": 31, + "green": 32, + "yellow": 33, + "blue": 34, + "magenta": 35, + "cyan": 36, + "white": 37, + "reset": 39, + "bright_black": 90, + "bright_red": 91, + "bright_green": 92, + "bright_yellow": 93, + "bright_blue": 94, + "bright_magenta": 95, + "bright_cyan": 96, + "bright_white": 97, +} +_ansi_reset_all = "\033[0m" + + +def hidden_prompt_func(prompt: str) -> str: + import getpass + + return getpass.getpass(prompt) + + +def _build_prompt( + text: str, + suffix: str, + show_default: bool = False, + default: t.Optional[t.Any] = None, + show_choices: bool = True, + type: t.Optional[ParamType] = None, +) -> str: + prompt = text + if type is not None and show_choices and isinstance(type, Choice): + prompt += f" ({', '.join(map(str, type.choices))})" + if default is not None and show_default: + prompt = f"{prompt} [{_format_default(default)}]" + return f"{prompt}{suffix}" + + +def _format_default(default: t.Any) -> t.Any: + if isinstance(default, (io.IOBase, LazyFile)) and hasattr(default, "name"): + return default.name # type: ignore + + return default + + +def prompt( + text: str, + default: t.Optional[t.Any] = None, + hide_input: bool = False, + confirmation_prompt: t.Union[bool, str] = False, + type: t.Optional[t.Union[ParamType, t.Any]] = None, + value_proc: t.Optional[t.Callable[[str], t.Any]] = None, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, + show_choices: bool = True, +) -> t.Any: + """Prompts a user for input. This is a convenience function that can + be used to prompt a user for input later. + + If the user aborts the input by sending a interrupt signal, this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the text to show for the prompt. + :param default: the default value to use if no input happens. If this + is not given it will prompt until it's aborted. + :param hide_input: if this is set to true then the input value will + be hidden. + :param confirmation_prompt: Prompt a second time to confirm the + value. Can be set to a string instead of ``True`` to customize + the message. + :param type: the type to use to check the value against. + :param value_proc: if this parameter is provided it's a function that + is invoked instead of the type conversion to + convert a value. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + :param show_choices: Show or hide choices if the passed type is a Choice. + For example if type is a Choice of either day or week, + show_choices is true and text is "Group by" then the + prompt will be "Group by (day, week): ". + + .. versionadded:: 8.0 + ``confirmation_prompt`` can be a custom string. + + .. versionadded:: 7.0 + Added the ``show_choices`` parameter. + + .. versionadded:: 6.0 + Added unicode support for cmd.exe on Windows. + + .. versionadded:: 4.0 + Added the `err` parameter. + + """ + + def prompt_func(text: str) -> str: + f = hidden_prompt_func if hide_input else visible_prompt_func + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(text.rstrip(" "), nl=False, err=err) + # Echo a space to stdout to work around an issue where + # readline causes backspace to clear the whole line. + return f(" ") + except (KeyboardInterrupt, EOFError): + # getpass doesn't print a newline if the user aborts input with ^C. + # Allegedly this behavior is inherited from getpass(3). + # A doc bug has been filed at https://bugs.python.org/issue24711 + if hide_input: + echo(None, err=err) + raise Abort() from None + + if value_proc is None: + value_proc = convert_type(type, default) + + prompt = _build_prompt( + text, prompt_suffix, show_default, default, show_choices, type + ) + + if confirmation_prompt: + if confirmation_prompt is True: + confirmation_prompt = _("Repeat for confirmation") + + confirmation_prompt = t.cast(str, confirmation_prompt) + confirmation_prompt = _build_prompt(confirmation_prompt, prompt_suffix) + + while True: + while True: + value = prompt_func(prompt) + if value: + break + elif default is not None: + value = default + break + try: + result = value_proc(value) + except UsageError as e: + if hide_input: + echo(_("Error: The value you entered was invalid."), err=err) + else: + echo(_("Error: {e.message}").format(e=e), err=err) # noqa: B306 + continue + if not confirmation_prompt: + return result + while True: + confirmation_prompt = t.cast(str, confirmation_prompt) + value2 = prompt_func(confirmation_prompt) + if value2: + break + if value == value2: + return result + echo(_("Error: The two entered values do not match."), err=err) + + +def confirm( + text: str, + default: t.Optional[bool] = False, + abort: bool = False, + prompt_suffix: str = ": ", + show_default: bool = True, + err: bool = False, +) -> bool: + """Prompts for confirmation (yes/no question). + + If the user aborts the input by sending a interrupt signal this + function will catch it and raise a :exc:`Abort` exception. + + :param text: the question to ask. + :param default: The default value to use when no input is given. If + ``None``, repeat until input is given. + :param abort: if this is set to `True` a negative answer aborts the + exception by raising :exc:`Abort`. + :param prompt_suffix: a suffix that should be added to the prompt. + :param show_default: shows or hides the default value in the prompt. + :param err: if set to true the file defaults to ``stderr`` instead of + ``stdout``, the same as with echo. + + .. versionchanged:: 8.0 + Repeat until input is given if ``default`` is ``None``. + + .. versionadded:: 4.0 + Added the ``err`` parameter. + """ + prompt = _build_prompt( + text, + prompt_suffix, + show_default, + "y/n" if default is None else ("Y/n" if default else "y/N"), + ) + + while True: + try: + # Write the prompt separately so that we get nice + # coloring through colorama on Windows + echo(prompt.rstrip(" "), nl=False, err=err) + # Echo a space to stdout to work around an issue where + # readline causes backspace to clear the whole line. + value = visible_prompt_func(" ").lower().strip() + except (KeyboardInterrupt, EOFError): + raise Abort() from None + if value in ("y", "yes"): + rv = True + elif value in ("n", "no"): + rv = False + elif default is not None and value == "": + rv = default + else: + echo(_("Error: invalid input"), err=err) + continue + break + if abort and not rv: + raise Abort() + return rv + + +def get_terminal_size() -> os.terminal_size: + """Returns the current size of the terminal as tuple in the form + ``(width, height)`` in columns and rows. + + .. deprecated:: 8.0 + Will be removed in Click 8.1. Use + :func:`shutil.get_terminal_size` instead. + """ + import shutil + import warnings + + warnings.warn( + "'click.get_terminal_size()' is deprecated and will be removed" + " in Click 8.1. Use 'shutil.get_terminal_size()' instead.", + DeprecationWarning, + stacklevel=2, + ) + return shutil.get_terminal_size() + + +def echo_via_pager( + text_or_generator: t.Union[t.Iterable[str], t.Callable[[], t.Iterable[str]], str], + color: t.Optional[bool] = None, +) -> None: + """This function takes a text and shows it via an environment specific + pager on stdout. + + .. versionchanged:: 3.0 + Added the `color` flag. + + :param text_or_generator: the text to page, or alternatively, a + generator emitting the text to page. + :param color: controls if the pager supports ANSI colors or not. The + default is autodetection. + """ + color = resolve_color_default(color) + + if inspect.isgeneratorfunction(text_or_generator): + i = t.cast(t.Callable[[], t.Iterable[str]], text_or_generator)() + elif isinstance(text_or_generator, str): + i = [text_or_generator] + else: + i = iter(t.cast(t.Iterable[str], text_or_generator)) + + # convert every element of i to a text type if necessary + text_generator = (el if isinstance(el, str) else str(el) for el in i) + + from ._termui_impl import pager + + return pager(itertools.chain(text_generator, "\n"), color) + + +def progressbar( + iterable: t.Optional[t.Iterable[V]] = None, + length: t.Optional[int] = None, + label: t.Optional[str] = None, + show_eta: bool = True, + show_percent: t.Optional[bool] = None, + show_pos: bool = False, + item_show_func: t.Optional[t.Callable[[t.Optional[V]], t.Optional[str]]] = None, + fill_char: str = "#", + empty_char: str = "-", + bar_template: str = "%(label)s [%(bar)s] %(info)s", + info_sep: str = " ", + width: int = 36, + file: t.Optional[t.TextIO] = None, + color: t.Optional[bool] = None, + update_min_steps: int = 1, +) -> "ProgressBar[V]": + """This function creates an iterable context manager that can be used + to iterate over something while showing a progress bar. It will + either iterate over the `iterable` or `length` items (that are counted + up). While iteration happens, this function will print a rendered + progress bar to the given `file` (defaults to stdout) and will attempt + to calculate remaining time and more. By default, this progress bar + will not be rendered if the file is not a terminal. + + The context manager creates the progress bar. When the context + manager is entered the progress bar is already created. With every + iteration over the progress bar, the iterable passed to the bar is + advanced and the bar is updated. When the context manager exits, + a newline is printed and the progress bar is finalized on screen. + + Note: The progress bar is currently designed for use cases where the + total progress can be expected to take at least several seconds. + Because of this, the ProgressBar class object won't display + progress that is considered too fast, and progress where the time + between steps is less than a second. + + No printing must happen or the progress bar will be unintentionally + destroyed. + + Example usage:: + + with progressbar(items) as bar: + for item in bar: + do_something_with(item) + + Alternatively, if no iterable is specified, one can manually update the + progress bar through the `update()` method instead of directly + iterating over the progress bar. The update method accepts the number + of steps to increment the bar with:: + + with progressbar(length=chunks.total_bytes) as bar: + for chunk in chunks: + process_chunk(chunk) + bar.update(chunks.bytes) + + The ``update()`` method also takes an optional value specifying the + ``current_item`` at the new position. This is useful when used + together with ``item_show_func`` to customize the output for each + manual step:: + + with click.progressbar( + length=total_size, + label='Unzipping archive', + item_show_func=lambda a: a.filename + ) as bar: + for archive in zip_file: + archive.extract() + bar.update(archive.size, archive) + + :param iterable: an iterable to iterate over. If not provided the length + is required. + :param length: the number of items to iterate over. By default the + progressbar will attempt to ask the iterator about its + length, which might or might not work. If an iterable is + also provided this parameter can be used to override the + length. If an iterable is not provided the progress bar + will iterate over a range of that length. + :param label: the label to show next to the progress bar. + :param show_eta: enables or disables the estimated time display. This is + automatically disabled if the length cannot be + determined. + :param show_percent: enables or disables the percentage display. The + default is `True` if the iterable has a length or + `False` if not. + :param show_pos: enables or disables the absolute position display. The + default is `False`. + :param item_show_func: A function called with the current item which + can return a string to show next to the progress bar. If the + function returns ``None`` nothing is shown. The current item can + be ``None``, such as when entering and exiting the bar. + :param fill_char: the character to use to show the filled part of the + progress bar. + :param empty_char: the character to use to show the non-filled part of + the progress bar. + :param bar_template: the format string to use as template for the bar. + The parameters in it are ``label`` for the label, + ``bar`` for the progress bar and ``info`` for the + info section. + :param info_sep: the separator between multiple info items (eta etc.) + :param width: the width of the progress bar in characters, 0 means full + terminal width + :param file: The file to write to. If this is not a terminal then + only the label is printed. + :param color: controls if the terminal supports ANSI colors or not. The + default is autodetection. This is only needed if ANSI + codes are included anywhere in the progress bar output + which is not the case by default. + :param update_min_steps: Render only when this many updates have + completed. This allows tuning for very fast iterators. + + .. versionchanged:: 8.0 + Output is shown even if execution time is less than 0.5 seconds. + + .. versionchanged:: 8.0 + ``item_show_func`` shows the current item, not the previous one. + + .. versionchanged:: 8.0 + Labels are echoed if the output is not a TTY. Reverts a change + in 7.0 that removed all output. + + .. versionadded:: 8.0 + Added the ``update_min_steps`` parameter. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. Added the ``update`` method to + the object. + + .. versionadded:: 2.0 + """ + from ._termui_impl import ProgressBar + + color = resolve_color_default(color) + return ProgressBar( + iterable=iterable, + length=length, + show_eta=show_eta, + show_percent=show_percent, + show_pos=show_pos, + item_show_func=item_show_func, + fill_char=fill_char, + empty_char=empty_char, + bar_template=bar_template, + info_sep=info_sep, + file=file, + label=label, + width=width, + color=color, + update_min_steps=update_min_steps, + ) + + +def clear() -> None: + """Clears the terminal screen. This will have the effect of clearing + the whole visible space of the terminal and moving the cursor to the + top left. This does not do anything if not connected to a terminal. + + .. versionadded:: 2.0 + """ + if not isatty(sys.stdout): + return + if WIN: + os.system("cls") + else: + sys.stdout.write("\033[2J\033[1;1H") + + +def _interpret_color( + color: t.Union[int, t.Tuple[int, int, int], str], offset: int = 0 +) -> str: + if isinstance(color, int): + return f"{38 + offset};5;{color:d}" + + if isinstance(color, (tuple, list)): + r, g, b = color + return f"{38 + offset};2;{r:d};{g:d};{b:d}" + + return str(_ansi_colors[color] + offset) + + +def style( + text: t.Any, + fg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, + bg: t.Optional[t.Union[int, t.Tuple[int, int, int], str]] = None, + bold: t.Optional[bool] = None, + dim: t.Optional[bool] = None, + underline: t.Optional[bool] = None, + overline: t.Optional[bool] = None, + italic: t.Optional[bool] = None, + blink: t.Optional[bool] = None, + reverse: t.Optional[bool] = None, + strikethrough: t.Optional[bool] = None, + reset: bool = True, +) -> str: + """Styles a text with ANSI styles and returns the new string. By + default the styling is self contained which means that at the end + of the string a reset code is issued. This can be prevented by + passing ``reset=False``. + + Examples:: + + click.echo(click.style('Hello World!', fg='green')) + click.echo(click.style('ATTENTION!', blink=True)) + click.echo(click.style('Some things', reverse=True, fg='cyan')) + click.echo(click.style('More colors', fg=(255, 12, 128), bg=117)) + + Supported color names: + + * ``black`` (might be a gray) + * ``red`` + * ``green`` + * ``yellow`` (might be an orange) + * ``blue`` + * ``magenta`` + * ``cyan`` + * ``white`` (might be light gray) + * ``bright_black`` + * ``bright_red`` + * ``bright_green`` + * ``bright_yellow`` + * ``bright_blue`` + * ``bright_magenta`` + * ``bright_cyan`` + * ``bright_white`` + * ``reset`` (reset the color code only) + + If the terminal supports it, color may also be specified as: + + - An integer in the interval [0, 255]. The terminal must support + 8-bit/256-color mode. + - An RGB tuple of three integers in [0, 255]. The terminal must + support 24-bit/true-color mode. + + See https://en.wikipedia.org/wiki/ANSI_color and + https://gist.github.com/XVilka/8346728 for more information. + + :param text: the string to style with ansi codes. + :param fg: if provided this will become the foreground color. + :param bg: if provided this will become the background color. + :param bold: if provided this will enable or disable bold mode. + :param dim: if provided this will enable or disable dim mode. This is + badly supported. + :param underline: if provided this will enable or disable underline. + :param overline: if provided this will enable or disable overline. + :param italic: if provided this will enable or disable italic. + :param blink: if provided this will enable or disable blinking. + :param reverse: if provided this will enable or disable inverse + rendering (foreground becomes background and the + other way round). + :param strikethrough: if provided this will enable or disable + striking through text. + :param reset: by default a reset-all code is added at the end of the + string which means that styles do not carry over. This + can be disabled to compose styles. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. + + .. versionchanged:: 8.0 + Added support for 256 and RGB color codes. + + .. versionchanged:: 8.0 + Added the ``strikethrough``, ``italic``, and ``overline`` + parameters. + + .. versionchanged:: 7.0 + Added support for bright colors. + + .. versionadded:: 2.0 + """ + if not isinstance(text, str): + text = str(text) + + bits = [] + + if fg: + try: + bits.append(f"\033[{_interpret_color(fg)}m") + except KeyError: + raise TypeError(f"Unknown color {fg!r}") from None + + if bg: + try: + bits.append(f"\033[{_interpret_color(bg, 10)}m") + except KeyError: + raise TypeError(f"Unknown color {bg!r}") from None + + if bold is not None: + bits.append(f"\033[{1 if bold else 22}m") + if dim is not None: + bits.append(f"\033[{2 if dim else 22}m") + if underline is not None: + bits.append(f"\033[{4 if underline else 24}m") + if overline is not None: + bits.append(f"\033[{53 if overline else 55}m") + if italic is not None: + bits.append(f"\033[{3 if italic else 23}m") + if blink is not None: + bits.append(f"\033[{5 if blink else 25}m") + if reverse is not None: + bits.append(f"\033[{7 if reverse else 27}m") + if strikethrough is not None: + bits.append(f"\033[{9 if strikethrough else 29}m") + bits.append(text) + if reset: + bits.append(_ansi_reset_all) + return "".join(bits) + + +def unstyle(text: str) -> str: + """Removes ANSI styling information from a string. Usually it's not + necessary to use this function as Click's echo function will + automatically remove styling if necessary. + + .. versionadded:: 2.0 + + :param text: the text to remove style information from. + """ + return strip_ansi(text) + + +def secho( + message: t.Optional[t.Any] = None, + file: t.Optional[t.IO] = None, + nl: bool = True, + err: bool = False, + color: t.Optional[bool] = None, + **styles: t.Any, +) -> None: + """This function combines :func:`echo` and :func:`style` into one + call. As such the following two calls are the same:: + + click.secho('Hello World!', fg='green') + click.echo(click.style('Hello World!', fg='green')) + + All keyword arguments are forwarded to the underlying functions + depending on which one they go with. + + Non-string types will be converted to :class:`str`. However, + :class:`bytes` are passed directly to :meth:`echo` without applying + style. If you want to style bytes that represent text, call + :meth:`bytes.decode` first. + + .. versionchanged:: 8.0 + A non-string ``message`` is converted to a string. Bytes are + passed through without style applied. + + .. versionadded:: 2.0 + """ + if message is not None and not isinstance(message, (bytes, bytearray)): + message = style(message, **styles) + + return echo(message, file=file, nl=nl, err=err, color=color) + + +def edit( + text: t.Optional[t.AnyStr] = None, + editor: t.Optional[str] = None, + env: t.Optional[t.Mapping[str, str]] = None, + require_save: bool = True, + extension: str = ".txt", + filename: t.Optional[str] = None, +) -> t.Optional[t.AnyStr]: + r"""Edits the given text in the defined editor. If an editor is given + (should be the full path to the executable but the regular operating + system search path is used for finding the executable) it overrides + the detected editor. Optionally, some environment variables can be + used. If the editor is closed without changes, `None` is returned. In + case a file is edited directly the return value is always `None` and + `require_save` and `extension` are ignored. + + If the editor cannot be opened a :exc:`UsageError` is raised. + + Note for Windows: to simplify cross-platform usage, the newlines are + automatically converted from POSIX to Windows and vice versa. As such, + the message here will have ``\n`` as newline markers. + + :param text: the text to edit. + :param editor: optionally the editor to use. Defaults to automatic + detection. + :param env: environment variables to forward to the editor. + :param require_save: if this is true, then not saving in the editor + will make the return value become `None`. + :param extension: the extension to tell the editor about. This defaults + to `.txt` but changing this might change syntax + highlighting. + :param filename: if provided it will edit this file instead of the + provided text contents. It will not use a temporary + file as an indirection in that case. + """ + from ._termui_impl import Editor + + ed = Editor(editor=editor, env=env, require_save=require_save, extension=extension) + + if filename is None: + return ed.edit(text) + + ed.edit_file(filename) + return None + + +def launch(url: str, wait: bool = False, locate: bool = False) -> int: + """This function launches the given URL (or filename) in the default + viewer application for this file type. If this is an executable, it + might launch the executable in a new session. The return value is + the exit code of the launched application. Usually, ``0`` indicates + success. + + Examples:: + + click.launch('https://click.palletsprojects.com/') + click.launch('/my/downloaded/file', locate=True) + + .. versionadded:: 2.0 + + :param url: URL or filename of the thing to launch. + :param wait: Wait for the program to exit before returning. This + only works if the launched program blocks. In particular, + ``xdg-open`` on Linux does not block. + :param locate: if this is set to `True` then instead of launching the + application associated with the URL it will attempt to + launch a file manager with the file located. This + might have weird effects if the URL does not point to + the filesystem. + """ + from ._termui_impl import open_url + + return open_url(url, wait=wait, locate=locate) + + +# If this is provided, getchar() calls into this instead. This is used +# for unittesting purposes. +_getchar: t.Optional[t.Callable[[bool], str]] = None + + +def getchar(echo: bool = False) -> str: + """Fetches a single character from the terminal and returns it. This + will always return a unicode character and under certain rare + circumstances this might return more than one character. The + situations which more than one character is returned is when for + whatever reason multiple characters end up in the terminal buffer or + standard input was not actually a terminal. + + Note that this will always read from the terminal, even if something + is piped into the standard input. + + Note for Windows: in rare cases when typing non-ASCII characters, this + function might wait for a second character and then return both at once. + This is because certain Unicode characters look like special-key markers. + + .. versionadded:: 2.0 + + :param echo: if set to `True`, the character read will also show up on + the terminal. The default is to not show it. + """ + global _getchar + + if _getchar is None: + from ._termui_impl import getchar as f + + _getchar = f + + return _getchar(echo) + + +def raw_terminal() -> t.ContextManager[int]: + from ._termui_impl import raw_terminal as f + + return f() + + +def pause(info: t.Optional[str] = None, err: bool = False) -> None: + """This command stops execution and waits for the user to press any + key to continue. This is similar to the Windows batch "pause" + command. If the program is not run through a terminal, this command + will instead do nothing. + + .. versionadded:: 2.0 + + .. versionadded:: 4.0 + Added the `err` parameter. + + :param info: The message to print before pausing. Defaults to + ``"Press any key to continue..."``. + :param err: if set to message goes to ``stderr`` instead of + ``stdout``, the same as with echo. + """ + if not isatty(sys.stdin) or not isatty(sys.stdout): + return + + if info is None: + info = _("Press any key to continue...") + + try: + if info: + echo(info, nl=False, err=err) + try: + getchar() + except (KeyboardInterrupt, EOFError): + pass + finally: + if info: + echo(err=err) diff --git a/flask-app/venv/lib/python3.8/site-packages/click/testing.py b/flask-app/venv/lib/python3.8/site-packages/click/testing.py new file mode 100644 index 000000000..d19b850fc --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click/testing.py @@ -0,0 +1,479 @@ +import contextlib +import io +import os +import shlex +import shutil +import sys +import tempfile +import typing as t +from types import TracebackType + +from . import formatting +from . import termui +from . import utils +from ._compat import _find_binary_reader + +if t.TYPE_CHECKING: + from .core import BaseCommand + + +class EchoingStdin: + def __init__(self, input: t.BinaryIO, output: t.BinaryIO) -> None: + self._input = input + self._output = output + self._paused = False + + def __getattr__(self, x: str) -> t.Any: + return getattr(self._input, x) + + def _echo(self, rv: bytes) -> bytes: + if not self._paused: + self._output.write(rv) + + return rv + + def read(self, n: int = -1) -> bytes: + return self._echo(self._input.read(n)) + + def read1(self, n: int = -1) -> bytes: + return self._echo(self._input.read1(n)) # type: ignore + + def readline(self, n: int = -1) -> bytes: + return self._echo(self._input.readline(n)) + + def readlines(self) -> t.List[bytes]: + return [self._echo(x) for x in self._input.readlines()] + + def __iter__(self) -> t.Iterator[bytes]: + return iter(self._echo(x) for x in self._input) + + def __repr__(self) -> str: + return repr(self._input) + + +@contextlib.contextmanager +def _pause_echo(stream: t.Optional[EchoingStdin]) -> t.Iterator[None]: + if stream is None: + yield + else: + stream._paused = True + yield + stream._paused = False + + +class _NamedTextIOWrapper(io.TextIOWrapper): + def __init__( + self, buffer: t.BinaryIO, name: str, mode: str, **kwargs: t.Any + ) -> None: + super().__init__(buffer, **kwargs) + self._name = name + self._mode = mode + + @property + def name(self) -> str: + return self._name + + @property + def mode(self) -> str: + return self._mode + + +def make_input_stream( + input: t.Optional[t.Union[str, bytes, t.IO]], charset: str +) -> t.BinaryIO: + # Is already an input stream. + if hasattr(input, "read"): + rv = _find_binary_reader(t.cast(t.IO, input)) + + if rv is not None: + return rv + + raise TypeError("Could not find binary reader for input stream.") + + if input is None: + input = b"" + elif isinstance(input, str): + input = input.encode(charset) + + return io.BytesIO(t.cast(bytes, input)) + + +class Result: + """Holds the captured result of an invoked CLI script.""" + + def __init__( + self, + runner: "CliRunner", + stdout_bytes: bytes, + stderr_bytes: t.Optional[bytes], + return_value: t.Any, + exit_code: int, + exception: t.Optional[BaseException], + exc_info: t.Optional[ + t.Tuple[t.Type[BaseException], BaseException, TracebackType] + ] = None, + ): + #: The runner that created the result + self.runner = runner + #: The standard output as bytes. + self.stdout_bytes = stdout_bytes + #: The standard error as bytes, or None if not available + self.stderr_bytes = stderr_bytes + #: The value returned from the invoked command. + #: + #: .. versionadded:: 8.0 + self.return_value = return_value + #: The exit code as integer. + self.exit_code = exit_code + #: The exception that happened if one did. + self.exception = exception + #: The traceback + self.exc_info = exc_info + + @property + def output(self) -> str: + """The (standard) output as unicode string.""" + return self.stdout + + @property + def stdout(self) -> str: + """The standard output as unicode string.""" + return self.stdout_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + @property + def stderr(self) -> str: + """The standard error as unicode string.""" + if self.stderr_bytes is None: + raise ValueError("stderr not separately captured") + return self.stderr_bytes.decode(self.runner.charset, "replace").replace( + "\r\n", "\n" + ) + + def __repr__(self) -> str: + exc_str = repr(self.exception) if self.exception else "okay" + return f"<{type(self).__name__} {exc_str}>" + + +class CliRunner: + """The CLI runner provides functionality to invoke a Click command line + script for unittesting purposes in a isolated environment. This only + works in single-threaded systems without any concurrency as it changes the + global interpreter state. + + :param charset: the character set for the input and output data. + :param env: a dictionary with environment variables for overriding. + :param echo_stdin: if this is set to `True`, then reading from stdin writes + to stdout. This is useful for showing examples in + some circumstances. Note that regular prompts + will automatically echo the input. + :param mix_stderr: if this is set to `False`, then stdout and stderr are + preserved as independent streams. This is useful for + Unix-philosophy apps that have predictable stdout and + noisy stderr, such that each may be measured + independently + """ + + def __init__( + self, + charset: str = "utf-8", + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + echo_stdin: bool = False, + mix_stderr: bool = True, + ) -> None: + self.charset = charset + self.env = env or {} + self.echo_stdin = echo_stdin + self.mix_stderr = mix_stderr + + def get_default_prog_name(self, cli: "BaseCommand") -> str: + """Given a command object it will return the default program name + for it. The default is the `name` attribute or ``"root"`` if not + set. + """ + return cli.name or "root" + + def make_env( + self, overrides: t.Optional[t.Mapping[str, t.Optional[str]]] = None + ) -> t.Mapping[str, t.Optional[str]]: + """Returns the environment overrides for invoking a script.""" + rv = dict(self.env) + if overrides: + rv.update(overrides) + return rv + + @contextlib.contextmanager + def isolation( + self, + input: t.Optional[t.Union[str, bytes, t.IO]] = None, + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + color: bool = False, + ) -> t.Iterator[t.Tuple[io.BytesIO, t.Optional[io.BytesIO]]]: + """A context manager that sets up the isolation for invoking of a + command line tool. This sets up stdin with the given input data + and `os.environ` with the overrides from the given dictionary. + This also rebinds some internals in Click to be mocked (like the + prompt functionality). + + This is automatically done in the :meth:`invoke` method. + + :param input: the input stream to put into sys.stdin. + :param env: the environment overrides as dictionary. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionchanged:: 8.0 + ``stderr`` is opened with ``errors="backslashreplace"`` + instead of the default ``"strict"``. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + """ + bytes_input = make_input_stream(input, self.charset) + echo_input = None + + old_stdin = sys.stdin + old_stdout = sys.stdout + old_stderr = sys.stderr + old_forced_width = formatting.FORCED_WIDTH + formatting.FORCED_WIDTH = 80 + + env = self.make_env(env) + + bytes_output = io.BytesIO() + + if self.echo_stdin: + bytes_input = echo_input = t.cast( + t.BinaryIO, EchoingStdin(bytes_input, bytes_output) + ) + + sys.stdin = text_input = _NamedTextIOWrapper( + bytes_input, encoding=self.charset, name="", mode="r" + ) + + if self.echo_stdin: + # Force unbuffered reads, otherwise TextIOWrapper reads a + # large chunk which is echoed early. + text_input._CHUNK_SIZE = 1 # type: ignore + + sys.stdout = _NamedTextIOWrapper( + bytes_output, encoding=self.charset, name="", mode="w" + ) + + bytes_error = None + if self.mix_stderr: + sys.stderr = sys.stdout + else: + bytes_error = io.BytesIO() + sys.stderr = _NamedTextIOWrapper( + bytes_error, + encoding=self.charset, + name="", + mode="w", + errors="backslashreplace", + ) + + @_pause_echo(echo_input) # type: ignore + def visible_input(prompt: t.Optional[str] = None) -> str: + sys.stdout.write(prompt or "") + val = text_input.readline().rstrip("\r\n") + sys.stdout.write(f"{val}\n") + sys.stdout.flush() + return val + + @_pause_echo(echo_input) # type: ignore + def hidden_input(prompt: t.Optional[str] = None) -> str: + sys.stdout.write(f"{prompt or ''}\n") + sys.stdout.flush() + return text_input.readline().rstrip("\r\n") + + @_pause_echo(echo_input) # type: ignore + def _getchar(echo: bool) -> str: + char = sys.stdin.read(1) + + if echo: + sys.stdout.write(char) + + sys.stdout.flush() + return char + + default_color = color + + def should_strip_ansi( + stream: t.Optional[t.IO] = None, color: t.Optional[bool] = None + ) -> bool: + if color is None: + return not default_color + return not color + + old_visible_prompt_func = termui.visible_prompt_func + old_hidden_prompt_func = termui.hidden_prompt_func + old__getchar_func = termui._getchar + old_should_strip_ansi = utils.should_strip_ansi # type: ignore + termui.visible_prompt_func = visible_input + termui.hidden_prompt_func = hidden_input + termui._getchar = _getchar + utils.should_strip_ansi = should_strip_ansi # type: ignore + + old_env = {} + try: + for key, value in env.items(): + old_env[key] = os.environ.get(key) + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + yield (bytes_output, bytes_error) + finally: + for key, value in old_env.items(): + if value is None: + try: + del os.environ[key] + except Exception: + pass + else: + os.environ[key] = value + sys.stdout = old_stdout + sys.stderr = old_stderr + sys.stdin = old_stdin + termui.visible_prompt_func = old_visible_prompt_func + termui.hidden_prompt_func = old_hidden_prompt_func + termui._getchar = old__getchar_func + utils.should_strip_ansi = old_should_strip_ansi # type: ignore + formatting.FORCED_WIDTH = old_forced_width + + def invoke( + self, + cli: "BaseCommand", + args: t.Optional[t.Union[str, t.Sequence[str]]] = None, + input: t.Optional[t.Union[str, bytes, t.IO]] = None, + env: t.Optional[t.Mapping[str, t.Optional[str]]] = None, + catch_exceptions: bool = True, + color: bool = False, + **extra: t.Any, + ) -> Result: + """Invokes a command in an isolated environment. The arguments are + forwarded directly to the command line script, the `extra` keyword + arguments are passed to the :meth:`~clickpkg.Command.main` function of + the command. + + This returns a :class:`Result` object. + + :param cli: the command to invoke + :param args: the arguments to invoke. It may be given as an iterable + or a string. When given as string it will be interpreted + as a Unix shell command. More details at + :func:`shlex.split`. + :param input: the input data for `sys.stdin`. + :param env: the environment overrides. + :param catch_exceptions: Whether to catch any other exceptions than + ``SystemExit``. + :param extra: the keyword arguments to pass to :meth:`main`. + :param color: whether the output should contain color codes. The + application can still override this explicitly. + + .. versionchanged:: 8.0 + The result object has the ``return_value`` attribute with + the value returned from the invoked command. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionchanged:: 3.0 + Added the ``catch_exceptions`` parameter. + + .. versionchanged:: 3.0 + The result object has the ``exc_info`` attribute with the + traceback if available. + """ + exc_info = None + with self.isolation(input=input, env=env, color=color) as outstreams: + return_value = None + exception: t.Optional[BaseException] = None + exit_code = 0 + + if isinstance(args, str): + args = shlex.split(args) + + try: + prog_name = extra.pop("prog_name") + except KeyError: + prog_name = self.get_default_prog_name(cli) + + try: + return_value = cli.main(args=args or (), prog_name=prog_name, **extra) + except SystemExit as e: + exc_info = sys.exc_info() + e_code = t.cast(t.Optional[t.Union[int, t.Any]], e.code) + + if e_code is None: + e_code = 0 + + if e_code != 0: + exception = e + + if not isinstance(e_code, int): + sys.stdout.write(str(e_code)) + sys.stdout.write("\n") + e_code = 1 + + exit_code = e_code + + except Exception as e: + if not catch_exceptions: + raise + exception = e + exit_code = 1 + exc_info = sys.exc_info() + finally: + sys.stdout.flush() + stdout = outstreams[0].getvalue() + if self.mix_stderr: + stderr = None + else: + stderr = outstreams[1].getvalue() # type: ignore + + return Result( + runner=self, + stdout_bytes=stdout, + stderr_bytes=stderr, + return_value=return_value, + exit_code=exit_code, + exception=exception, + exc_info=exc_info, # type: ignore + ) + + @contextlib.contextmanager + def isolated_filesystem( + self, temp_dir: t.Optional[t.Union[str, os.PathLike]] = None + ) -> t.Iterator[str]: + """A context manager that creates a temporary directory and + changes the current working directory to it. This isolates tests + that affect the contents of the CWD to prevent them from + interfering with each other. + + :param temp_dir: Create the temporary directory under this + directory. If given, the created directory is not removed + when exiting. + + .. versionchanged:: 8.0 + Added the ``temp_dir`` parameter. + """ + cwd = os.getcwd() + t = tempfile.mkdtemp(dir=temp_dir) + os.chdir(t) + + try: + yield t + finally: + os.chdir(cwd) + + if temp_dir is None: + try: + shutil.rmtree(t) + except OSError: # noqa: B014 + pass diff --git a/flask-app/venv/lib/python3.8/site-packages/click/types.py b/flask-app/venv/lib/python3.8/site-packages/click/types.py new file mode 100644 index 000000000..103d21829 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click/types.py @@ -0,0 +1,1052 @@ +import os +import stat +import typing as t +from datetime import datetime +from gettext import gettext as _ +from gettext import ngettext + +from ._compat import _get_argv_encoding +from ._compat import get_filesystem_encoding +from ._compat import open_stream +from .exceptions import BadParameter +from .utils import LazyFile +from .utils import safecall + +if t.TYPE_CHECKING: + import typing_extensions as te + from .core import Context + from .core import Parameter + from .shell_completion import CompletionItem + + +class ParamType: + """Represents the type of a parameter. Validates and converts values + from the command line or Python into the correct type. + + To implement a custom type, subclass and implement at least the + following: + + - The :attr:`name` class attribute must be set. + - Calling an instance of the type with ``None`` must return + ``None``. This is already implemented by default. + - :meth:`convert` must convert string values to the correct type. + - :meth:`convert` must accept values that are already the correct + type. + - It must be able to convert a value if the ``ctx`` and ``param`` + arguments are ``None``. This can occur when converting prompt + input. + """ + + is_composite: t.ClassVar[bool] = False + arity: t.ClassVar[int] = 1 + + #: the descriptive name of this type + name: str + + #: if a list of this type is expected and the value is pulled from a + #: string environment variable, this is what splits it up. `None` + #: means any whitespace. For all parameters the general rule is that + #: whitespace splits them up. The exception are paths and files which + #: are split by ``os.path.pathsep`` by default (":" on Unix and ";" on + #: Windows). + envvar_list_splitter: t.ClassVar[t.Optional[str]] = None + + def to_info_dict(self) -> t.Dict[str, t.Any]: + """Gather information that could be useful for a tool generating + user-facing documentation. + + Use :meth:`click.Context.to_info_dict` to traverse the entire + CLI structure. + + .. versionadded:: 8.0 + """ + # The class name without the "ParamType" suffix. + param_type = type(self).__name__.partition("ParamType")[0] + param_type = param_type.partition("ParameterType")[0] + return {"param_type": param_type, "name": self.name} + + def __call__( + self, + value: t.Any, + param: t.Optional["Parameter"] = None, + ctx: t.Optional["Context"] = None, + ) -> t.Any: + if value is not None: + return self.convert(value, param, ctx) + + def get_metavar(self, param: "Parameter") -> t.Optional[str]: + """Returns the metavar default for this param if it provides one.""" + + def get_missing_message(self, param: "Parameter") -> t.Optional[str]: + """Optionally might return extra information about a missing + parameter. + + .. versionadded:: 2.0 + """ + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + """Convert the value to the correct type. This is not called if + the value is ``None`` (the missing value). + + This must accept string values from the command line, as well as + values that are already the correct type. It may also convert + other compatible types. + + The ``param`` and ``ctx`` arguments may be ``None`` in certain + situations, such as when converting prompt input. + + If the value cannot be converted, call :meth:`fail` with a + descriptive message. + + :param value: The value to convert. + :param param: The parameter that is using this type to convert + its value. May be ``None``. + :param ctx: The current context that arrived at this value. May + be ``None``. + """ + return value + + def split_envvar_value(self, rv: str) -> t.Sequence[str]: + """Given a value from an environment variable this splits it up + into small chunks depending on the defined envvar list splitter. + + If the splitter is set to `None`, which means that whitespace splits, + then leading and trailing whitespace is ignored. Otherwise, leading + and trailing splitters usually lead to empty items being included. + """ + return (rv or "").split(self.envvar_list_splitter) + + def fail( + self, + message: str, + param: t.Optional["Parameter"] = None, + ctx: t.Optional["Context"] = None, + ) -> "t.NoReturn": + """Helper method to fail with an invalid value message.""" + raise BadParameter(message, ctx=ctx, param=param) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a list of + :class:`~click.shell_completion.CompletionItem` objects for the + incomplete value. Most types do not provide completions, but + some do, and this allows custom types to provide custom + completions as well. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + return [] + + +class CompositeParamType(ParamType): + is_composite = True + + @property + def arity(self) -> int: # type: ignore + raise NotImplementedError() + + +class FuncParamType(ParamType): + def __init__(self, func: t.Callable[[t.Any], t.Any]) -> None: + self.name = func.__name__ + self.func = func + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["func"] = self.func + return info_dict + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + return self.func(value) + except ValueError: + try: + value = str(value) + except UnicodeError: + value = value.decode("utf-8", "replace") + + self.fail(value, param, ctx) + + +class UnprocessedParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + return value + + def __repr__(self) -> str: + return "UNPROCESSED" + + +class StringParamType(ParamType): + name = "text" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if isinstance(value, bytes): + enc = _get_argv_encoding() + try: + value = value.decode(enc) + except UnicodeError: + fs_enc = get_filesystem_encoding() + if fs_enc != enc: + try: + value = value.decode(fs_enc) + except UnicodeError: + value = value.decode("utf-8", "replace") + else: + value = value.decode("utf-8", "replace") + return value + return str(value) + + def __repr__(self) -> str: + return "STRING" + + +class Choice(ParamType): + """The choice type allows a value to be checked against a fixed set + of supported values. All of these values have to be strings. + + You should only pass a list or tuple of choices. Other iterables + (like generators) may lead to surprising results. + + The resulting value will always be one of the originally passed choices + regardless of ``case_sensitive`` or any ``ctx.token_normalize_func`` + being specified. + + See :ref:`choice-opts` for an example. + + :param case_sensitive: Set to false to make choices case + insensitive. Defaults to true. + """ + + name = "choice" + + def __init__(self, choices: t.Sequence[str], case_sensitive: bool = True) -> None: + self.choices = choices + self.case_sensitive = case_sensitive + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["choices"] = self.choices + info_dict["case_sensitive"] = self.case_sensitive + return info_dict + + def get_metavar(self, param: "Parameter") -> str: + choices_str = "|".join(self.choices) + + # Use curly braces to indicate a required argument. + if param.required and param.param_type_name == "argument": + return f"{{{choices_str}}}" + + # Use square braces to indicate an option or optional argument. + return f"[{choices_str}]" + + def get_missing_message(self, param: "Parameter") -> str: + return _("Choose from:\n\t{choices}").format(choices=",\n\t".join(self.choices)) + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + # Match through normalization and case sensitivity + # first do token_normalize_func, then lowercase + # preserve original `value` to produce an accurate message in + # `self.fail` + normed_value = value + normed_choices = {choice: choice for choice in self.choices} + + if ctx is not None and ctx.token_normalize_func is not None: + normed_value = ctx.token_normalize_func(value) + normed_choices = { + ctx.token_normalize_func(normed_choice): original + for normed_choice, original in normed_choices.items() + } + + if not self.case_sensitive: + normed_value = normed_value.casefold() + normed_choices = { + normed_choice.casefold(): original + for normed_choice, original in normed_choices.items() + } + + if normed_value in normed_choices: + return normed_choices[normed_value] + + choices_str = ", ".join(map(repr, self.choices)) + self.fail( + ngettext( + "{value!r} is not {choice}.", + "{value!r} is not one of {choices}.", + len(self.choices), + ).format(value=value, choice=choices_str, choices=choices_str), + param, + ctx, + ) + + def __repr__(self) -> str: + return f"Choice({list(self.choices)})" + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Complete choices that start with the incomplete value. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + str_choices = map(str, self.choices) + + if self.case_sensitive: + matched = (c for c in str_choices if c.startswith(incomplete)) + else: + incomplete = incomplete.lower() + matched = (c for c in str_choices if c.lower().startswith(incomplete)) + + return [CompletionItem(c) for c in matched] + + +class DateTime(ParamType): + """The DateTime type converts date strings into `datetime` objects. + + The format strings which are checked are configurable, but default to some + common (non-timezone aware) ISO 8601 formats. + + When specifying *DateTime* formats, you should only pass a list or a tuple. + Other iterables, like generators, may lead to surprising results. + + The format strings are processed using ``datetime.strptime``, and this + consequently defines the format strings which are allowed. + + Parsing is tried using each format, in order, and the first format which + parses successfully is used. + + :param formats: A list or tuple of date format strings, in the order in + which they should be tried. Defaults to + ``'%Y-%m-%d'``, ``'%Y-%m-%dT%H:%M:%S'``, + ``'%Y-%m-%d %H:%M:%S'``. + """ + + name = "datetime" + + def __init__(self, formats: t.Optional[t.Sequence[str]] = None): + self.formats = formats or ["%Y-%m-%d", "%Y-%m-%dT%H:%M:%S", "%Y-%m-%d %H:%M:%S"] + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["formats"] = self.formats + return info_dict + + def get_metavar(self, param: "Parameter") -> str: + return f"[{'|'.join(self.formats)}]" + + def _try_to_convert_date(self, value: t.Any, format: str) -> t.Optional[datetime]: + try: + return datetime.strptime(value, format) + except ValueError: + return None + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if isinstance(value, datetime): + return value + + for format in self.formats: + converted = self._try_to_convert_date(value, format) + + if converted is not None: + return converted + + formats_str = ", ".join(map(repr, self.formats)) + self.fail( + ngettext( + "{value!r} does not match the format {format}.", + "{value!r} does not match the formats {formats}.", + len(self.formats), + ).format(value=value, format=formats_str, formats=formats_str), + param, + ctx, + ) + + def __repr__(self) -> str: + return "DateTime" + + +class _NumberParamTypeBase(ParamType): + _number_class: t.ClassVar[t.Type] + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + return self._number_class(value) + except ValueError: + self.fail( + _("{value!r} is not a valid {number_type}.").format( + value=value, number_type=self.name + ), + param, + ctx, + ) + + +class _NumberRangeBase(_NumberParamTypeBase): + def __init__( + self, + min: t.Optional[float] = None, + max: t.Optional[float] = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + self.min = min + self.max = max + self.min_open = min_open + self.max_open = max_open + self.clamp = clamp + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + min=self.min, + max=self.max, + min_open=self.min_open, + max_open=self.max_open, + clamp=self.clamp, + ) + return info_dict + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + import operator + + rv = super().convert(value, param, ctx) + lt_min: bool = self.min is not None and ( + operator.le if self.min_open else operator.lt + )(rv, self.min) + gt_max: bool = self.max is not None and ( + operator.ge if self.max_open else operator.gt + )(rv, self.max) + + if self.clamp: + if lt_min: + return self._clamp(self.min, 1, self.min_open) # type: ignore + + if gt_max: + return self._clamp(self.max, -1, self.max_open) # type: ignore + + if lt_min or gt_max: + self.fail( + _("{value} is not in the range {range}.").format( + value=rv, range=self._describe_range() + ), + param, + ctx, + ) + + return rv + + def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: + """Find the valid value to clamp to bound in the given + direction. + + :param bound: The boundary value. + :param dir: 1 or -1 indicating the direction to move. + :param open: If true, the range does not include the bound. + """ + raise NotImplementedError + + def _describe_range(self) -> str: + """Describe the range for use in help text.""" + if self.min is None: + op = "<" if self.max_open else "<=" + return f"x{op}{self.max}" + + if self.max is None: + op = ">" if self.min_open else ">=" + return f"x{op}{self.min}" + + lop = "<" if self.min_open else "<=" + rop = "<" if self.max_open else "<=" + return f"{self.min}{lop}x{rop}{self.max}" + + def __repr__(self) -> str: + clamp = " clamped" if self.clamp else "" + return f"<{type(self).__name__} {self._describe_range()}{clamp}>" + + +class IntParamType(_NumberParamTypeBase): + name = "integer" + _number_class = int + + def __repr__(self) -> str: + return "INT" + + +class IntRange(_NumberRangeBase, IntParamType): + """Restrict an :data:`click.INT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "integer range" + + def _clamp( # type: ignore + self, bound: int, dir: "te.Literal[1, -1]", open: bool + ) -> int: + if not open: + return bound + + return bound + dir + + +class FloatParamType(_NumberParamTypeBase): + name = "float" + _number_class = float + + def __repr__(self) -> str: + return "FLOAT" + + +class FloatRange(_NumberRangeBase, FloatParamType): + """Restrict a :data:`click.FLOAT` value to a range of accepted + values. See :ref:`ranges`. + + If ``min`` or ``max`` are not passed, any value is accepted in that + direction. If ``min_open`` or ``max_open`` are enabled, the + corresponding boundary is not included in the range. + + If ``clamp`` is enabled, a value outside the range is clamped to the + boundary instead of failing. This is not supported if either + boundary is marked ``open``. + + .. versionchanged:: 8.0 + Added the ``min_open`` and ``max_open`` parameters. + """ + + name = "float range" + + def __init__( + self, + min: t.Optional[float] = None, + max: t.Optional[float] = None, + min_open: bool = False, + max_open: bool = False, + clamp: bool = False, + ) -> None: + super().__init__( + min=min, max=max, min_open=min_open, max_open=max_open, clamp=clamp + ) + + if (min_open or max_open) and clamp: + raise TypeError("Clamping is not supported for open bounds.") + + def _clamp(self, bound: float, dir: "te.Literal[1, -1]", open: bool) -> float: + if not open: + return bound + + # Could use Python 3.9's math.nextafter here, but clamping an + # open float range doesn't seem to be particularly useful. It's + # left up to the user to write a callback to do it if needed. + raise RuntimeError("Clamping is not supported for open bounds.") + + +class BoolParamType(ParamType): + name = "boolean" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + if value in {False, True}: + return bool(value) + + norm = value.strip().lower() + + if norm in {"1", "true", "t", "yes", "y", "on"}: + return True + + if norm in {"0", "false", "f", "no", "n", "off"}: + return False + + self.fail( + _("{value!r} is not a valid boolean.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "BOOL" + + +class UUIDParameterType(ParamType): + name = "uuid" + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + import uuid + + if isinstance(value, uuid.UUID): + return value + + value = value.strip() + + try: + return uuid.UUID(value) + except ValueError: + self.fail( + _("{value!r} is not a valid UUID.").format(value=value), param, ctx + ) + + def __repr__(self) -> str: + return "UUID" + + +class File(ParamType): + """Declares a parameter to be a file for reading or writing. The file + is automatically closed once the context tears down (after the command + finished working). + + Files can be opened for reading or writing. The special value ``-`` + indicates stdin or stdout depending on the mode. + + By default, the file is opened for reading text data, but it can also be + opened in binary mode or for writing. The encoding parameter can be used + to force a specific encoding. + + The `lazy` flag controls if the file should be opened immediately or upon + first IO. The default is to be non-lazy for standard input and output + streams as well as files opened for reading, `lazy` otherwise. When opening a + file lazily for reading, it is still opened temporarily for validation, but + will not be held open until first IO. lazy is mainly useful when opening + for writing to avoid creating the file until it is needed. + + Starting with Click 2.0, files can also be opened atomically in which + case all writes go into a separate file in the same folder and upon + completion the file will be moved over to the original location. This + is useful if a file regularly read by other users is modified. + + See :ref:`file-args` for more information. + """ + + name = "filename" + envvar_list_splitter = os.path.pathsep + + def __init__( + self, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + lazy: t.Optional[bool] = None, + atomic: bool = False, + ) -> None: + self.mode = mode + self.encoding = encoding + self.errors = errors + self.lazy = lazy + self.atomic = atomic + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update(mode=self.mode, encoding=self.encoding) + return info_dict + + def resolve_lazy_flag(self, value: t.Any) -> bool: + if self.lazy is not None: + return self.lazy + if value == "-": + return False + elif "w" in self.mode: + return True + return False + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + try: + if hasattr(value, "read") or hasattr(value, "write"): + return value + + lazy = self.resolve_lazy_flag(value) + + if lazy: + f: t.IO = t.cast( + t.IO, + LazyFile( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ), + ) + + if ctx is not None: + ctx.call_on_close(f.close_intelligently) # type: ignore + + return f + + f, should_close = open_stream( + value, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + + # If a context is provided, we automatically close the file + # at the end of the context execution (or flush out). If a + # context does not exist, it's the caller's responsibility to + # properly close the file. This for instance happens when the + # type is used with prompts. + if ctx is not None: + if should_close: + ctx.call_on_close(safecall(f.close)) + else: + ctx.call_on_close(safecall(f.flush)) + + return f + except OSError as e: # noqa: B014 + self.fail(f"{os.fsdecode(value)!r}: {e.strerror}", param, ctx) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a special completion marker that tells the completion + system to use the shell to provide file path completions. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + return [CompletionItem(incomplete, type="file")] + + +class Path(ParamType): + """The path type is similar to the :class:`File` type but it performs + different checks. First of all, instead of returning an open file + handle it returns just the filename. Secondly, it can perform various + basic checks about what the file or directory should be. + + :param exists: if set to true, the file or directory needs to exist for + this value to be valid. If this is not required and a + file does indeed not exist, then all further checks are + silently skipped. + :param file_okay: controls if a file is a possible value. + :param dir_okay: controls if a directory is a possible value. + :param writable: if true, a writable check is performed. + :param readable: if true, a readable check is performed. + :param resolve_path: if this is true, then the path is fully resolved + before the value is passed onwards. This means + that it's absolute and symlinks are resolved. It + will not expand a tilde-prefix, as this is + supposed to be done by the shell only. + :param allow_dash: If this is set to `True`, a single dash to indicate + standard streams is permitted. + :param path_type: Convert the incoming path value to this type. If + ``None``, keep Python's default, which is ``str``. Useful to + convert to :class:`pathlib.Path`. + + .. versionchanged:: 8.0 + Allow passing ``type=pathlib.Path``. + + .. versionchanged:: 6.0 + Added the ``allow_dash`` parameter. + """ + + envvar_list_splitter = os.path.pathsep + + def __init__( + self, + exists: bool = False, + file_okay: bool = True, + dir_okay: bool = True, + writable: bool = False, + readable: bool = True, + resolve_path: bool = False, + allow_dash: bool = False, + path_type: t.Optional[t.Type] = None, + ): + self.exists = exists + self.file_okay = file_okay + self.dir_okay = dir_okay + self.writable = writable + self.readable = readable + self.resolve_path = resolve_path + self.allow_dash = allow_dash + self.type = path_type + + if self.file_okay and not self.dir_okay: + self.name = _("file") + elif self.dir_okay and not self.file_okay: + self.name = _("directory") + else: + self.name = _("path") + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict.update( + exists=self.exists, + file_okay=self.file_okay, + dir_okay=self.dir_okay, + writable=self.writable, + readable=self.readable, + allow_dash=self.allow_dash, + ) + return info_dict + + def coerce_path_result(self, rv: t.Any) -> t.Any: + if self.type is not None and not isinstance(rv, self.type): + if self.type is str: + rv = os.fsdecode(rv) + elif self.type is bytes: + rv = os.fsencode(rv) + else: + rv = self.type(rv) + + return rv + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + rv = value + + is_dash = self.file_okay and self.allow_dash and rv in (b"-", "-") + + if not is_dash: + if self.resolve_path: + # os.path.realpath doesn't resolve symlinks on Windows + # until Python 3.8. Use pathlib for now. + import pathlib + + rv = os.fsdecode(pathlib.Path(rv).resolve()) + + try: + st = os.stat(rv) + except OSError: + if not self.exists: + return self.coerce_path_result(rv) + self.fail( + _("{name} {filename!r} does not exist.").format( + name=self.name.title(), filename=os.fsdecode(value) + ), + param, + ctx, + ) + + if not self.file_okay and stat.S_ISREG(st.st_mode): + self.fail( + _("{name} {filename!r} is a file.").format( + name=self.name.title(), filename=os.fsdecode(value) + ), + param, + ctx, + ) + if not self.dir_okay and stat.S_ISDIR(st.st_mode): + self.fail( + _("{name} {filename!r} is a directory.").format( + name=self.name.title(), filename=os.fsdecode(value) + ), + param, + ctx, + ) + if self.writable and not os.access(rv, os.W_OK): + self.fail( + _("{name} {filename!r} is not writable.").format( + name=self.name.title(), filename=os.fsdecode(value) + ), + param, + ctx, + ) + if self.readable and not os.access(rv, os.R_OK): + self.fail( + _("{name} {filename!r} is not readable.").format( + name=self.name.title(), filename=os.fsdecode(value) + ), + param, + ctx, + ) + + return self.coerce_path_result(rv) + + def shell_complete( + self, ctx: "Context", param: "Parameter", incomplete: str + ) -> t.List["CompletionItem"]: + """Return a special completion marker that tells the completion + system to use the shell to provide path completions for only + directories or any paths. + + :param ctx: Invocation context for this command. + :param param: The parameter that is requesting completion. + :param incomplete: Value being completed. May be empty. + + .. versionadded:: 8.0 + """ + from click.shell_completion import CompletionItem + + type = "dir" if self.dir_okay and not self.file_okay else "file" + return [CompletionItem(incomplete, type=type)] + + +class Tuple(CompositeParamType): + """The default behavior of Click is to apply a type on a value directly. + This works well in most cases, except for when `nargs` is set to a fixed + count and different types should be used for different items. In this + case the :class:`Tuple` type can be used. This type can only be used + if `nargs` is set to a fixed number. + + For more information see :ref:`tuple-type`. + + This can be selected by using a Python tuple literal as a type. + + :param types: a list of types that should be used for the tuple items. + """ + + def __init__(self, types: t.Sequence[t.Union[t.Type, ParamType]]) -> None: + self.types = [convert_type(ty) for ty in types] + + def to_info_dict(self) -> t.Dict[str, t.Any]: + info_dict = super().to_info_dict() + info_dict["types"] = [t.to_info_dict() for t in self.types] + return info_dict + + @property + def name(self) -> str: # type: ignore + return f"<{' '.join(ty.name for ty in self.types)}>" + + @property + def arity(self) -> int: # type: ignore + return len(self.types) + + def convert( + self, value: t.Any, param: t.Optional["Parameter"], ctx: t.Optional["Context"] + ) -> t.Any: + len_type = len(self.types) + len_value = len(value) + + if len_value != len_type: + self.fail( + ngettext( + "{len_type} values are required, but {len_value} was given.", + "{len_type} values are required, but {len_value} were given.", + len_value, + ).format(len_type=len_type, len_value=len_value), + param=param, + ctx=ctx, + ) + + return tuple(ty(x, param, ctx) for ty, x in zip(self.types, value)) + + +def convert_type(ty: t.Optional[t.Any], default: t.Optional[t.Any] = None) -> ParamType: + """Find the most appropriate :class:`ParamType` for the given Python + type. If the type isn't provided, it can be inferred from a default + value. + """ + guessed_type = False + + if ty is None and default is not None: + if isinstance(default, (tuple, list)): + # If the default is empty, ty will remain None and will + # return STRING. + if default: + item = default[0] + + # A tuple of tuples needs to detect the inner types. + # Can't call convert recursively because that would + # incorrectly unwind the tuple to a single type. + if isinstance(item, (tuple, list)): + ty = tuple(map(type, item)) + else: + ty = type(item) + else: + ty = type(default) + + guessed_type = True + + if isinstance(ty, tuple): + return Tuple(ty) + + if isinstance(ty, ParamType): + return ty + + if ty is str or ty is None: + return STRING + + if ty is int: + return INT + + if ty is float: + return FLOAT + + if ty is bool: + return BOOL + + if guessed_type: + return STRING + + if __debug__: + try: + if issubclass(ty, ParamType): + raise AssertionError( + f"Attempted to use an uninstantiated parameter type ({ty})." + ) + except TypeError: + # ty is an instance (correct), so issubclass fails. + pass + + return FuncParamType(ty) + + +#: A dummy parameter type that just does nothing. From a user's +#: perspective this appears to just be the same as `STRING` but +#: internally no string conversion takes place if the input was bytes. +#: This is usually useful when working with file paths as they can +#: appear in bytes and unicode. +#: +#: For path related uses the :class:`Path` type is a better choice but +#: there are situations where an unprocessed type is useful which is why +#: it is is provided. +#: +#: .. versionadded:: 4.0 +UNPROCESSED = UnprocessedParamType() + +#: A unicode string parameter type which is the implicit default. This +#: can also be selected by using ``str`` as type. +STRING = StringParamType() + +#: An integer parameter. This can also be selected by using ``int`` as +#: type. +INT = IntParamType() + +#: A floating point value parameter. This can also be selected by using +#: ``float`` as type. +FLOAT = FloatParamType() + +#: A boolean parameter. This is the default for boolean flags. This can +#: also be selected by using ``bool`` as a type. +BOOL = BoolParamType() + +#: A UUID parameter. +UUID = UUIDParameterType() diff --git a/flask-app/venv/lib/python3.8/site-packages/click/utils.py b/flask-app/venv/lib/python3.8/site-packages/click/utils.py new file mode 100644 index 000000000..16033d623 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/click/utils.py @@ -0,0 +1,579 @@ +import os +import sys +import typing as t +from functools import update_wrapper +from types import ModuleType + +from ._compat import _default_text_stderr +from ._compat import _default_text_stdout +from ._compat import _find_binary_writer +from ._compat import auto_wrap_for_ansi +from ._compat import binary_streams +from ._compat import get_filesystem_encoding +from ._compat import open_stream +from ._compat import should_strip_ansi +from ._compat import strip_ansi +from ._compat import text_streams +from ._compat import WIN +from .globals import resolve_color_default + +if t.TYPE_CHECKING: + import typing_extensions as te + +F = t.TypeVar("F", bound=t.Callable[..., t.Any]) + + +def _posixify(name: str) -> str: + return "-".join(name.split()).lower() + + +def safecall(func: F) -> F: + """Wraps a function so that it swallows exceptions.""" + + def wrapper(*args, **kwargs): # type: ignore + try: + return func(*args, **kwargs) + except Exception: + pass + + return update_wrapper(t.cast(F, wrapper), func) + + +def make_str(value: t.Any) -> str: + """Converts a value into a valid string.""" + if isinstance(value, bytes): + try: + return value.decode(get_filesystem_encoding()) + except UnicodeError: + return value.decode("utf-8", "replace") + return str(value) + + +def make_default_short_help(help: str, max_length: int = 45) -> str: + """Returns a condensed version of help string.""" + # Consider only the first paragraph. + paragraph_end = help.find("\n\n") + + if paragraph_end != -1: + help = help[:paragraph_end] + + # Collapse newlines, tabs, and spaces. + words = help.split() + + if not words: + return "" + + # The first paragraph started with a "no rewrap" marker, ignore it. + if words[0] == "\b": + words = words[1:] + + total_length = 0 + last_index = len(words) - 1 + + for i, word in enumerate(words): + total_length += len(word) + (i > 0) + + if total_length > max_length: # too long, truncate + break + + if word[-1] == ".": # sentence end, truncate without "..." + return " ".join(words[: i + 1]) + + if total_length == max_length and i != last_index: + break # not at sentence end, truncate with "..." + else: + return " ".join(words) # no truncation needed + + # Account for the length of the suffix. + total_length += len("...") + + # remove words until the length is short enough + while i > 0: + total_length -= len(words[i]) + (i > 0) + + if total_length <= max_length: + break + + i -= 1 + + return " ".join(words[:i]) + "..." + + +class LazyFile: + """A lazy file works like a regular file but it does not fully open + the file but it does perform some basic checks early to see if the + filename parameter does make sense. This is useful for safely opening + files for writing. + """ + + def __init__( + self, + filename: str, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + atomic: bool = False, + ): + self.name = filename + self.mode = mode + self.encoding = encoding + self.errors = errors + self.atomic = atomic + self._f: t.Optional[t.IO] + + if filename == "-": + self._f, self.should_close = open_stream(filename, mode, encoding, errors) + else: + if "r" in mode: + # Open and close the file in case we're opening it for + # reading so that we can catch at least some errors in + # some cases early. + open(filename, mode).close() + self._f = None + self.should_close = True + + def __getattr__(self, name: str) -> t.Any: + return getattr(self.open(), name) + + def __repr__(self) -> str: + if self._f is not None: + return repr(self._f) + return f"" + + def open(self) -> t.IO: + """Opens the file if it's not yet open. This call might fail with + a :exc:`FileError`. Not handling this error will produce an error + that Click shows. + """ + if self._f is not None: + return self._f + try: + rv, self.should_close = open_stream( + self.name, self.mode, self.encoding, self.errors, atomic=self.atomic + ) + except OSError as e: # noqa: E402 + from .exceptions import FileError + + raise FileError(self.name, hint=e.strerror) from e + self._f = rv + return rv + + def close(self) -> None: + """Closes the underlying file, no matter what.""" + if self._f is not None: + self._f.close() + + def close_intelligently(self) -> None: + """This function only closes the file if it was opened by the lazy + file wrapper. For instance this will never close stdin. + """ + if self.should_close: + self.close() + + def __enter__(self) -> "LazyFile": + return self + + def __exit__(self, exc_type, exc_value, tb): # type: ignore + self.close_intelligently() + + def __iter__(self) -> t.Iterator[t.AnyStr]: + self.open() + return iter(self._f) # type: ignore + + +class KeepOpenFile: + def __init__(self, file: t.IO) -> None: + self._file = file + + def __getattr__(self, name: str) -> t.Any: + return getattr(self._file, name) + + def __enter__(self) -> "KeepOpenFile": + return self + + def __exit__(self, exc_type, exc_value, tb): # type: ignore + pass + + def __repr__(self) -> str: + return repr(self._file) + + def __iter__(self) -> t.Iterator[t.AnyStr]: + return iter(self._file) + + +def echo( + message: t.Optional[t.Any] = None, + file: t.Optional[t.IO] = None, + nl: bool = True, + err: bool = False, + color: t.Optional[bool] = None, +) -> None: + """Print a message and newline to stdout or a file. This should be + used instead of :func:`print` because it provides better support + for different data, files, and environments. + + Compared to :func:`print`, this does the following: + + - Ensures that the output encoding is not misconfigured on Linux. + - Supports Unicode in the Windows console. + - Supports writing to binary outputs, and supports writing bytes + to text outputs. + - Supports colors and styles on Windows. + - Removes ANSI color and style codes if the output does not look + like an interactive terminal. + - Always flushes the output. + + :param message: The string or bytes to output. Other objects are + converted to strings. + :param file: The file to write to. Defaults to ``stdout``. + :param err: Write to ``stderr`` instead of ``stdout``. + :param nl: Print a newline after the message. Enabled by default. + :param color: Force showing or hiding colors and other styles. By + default Click will remove color if the output does not look like + an interactive terminal. + + .. versionchanged:: 6.0 + Support Unicode output on the Windows console. Click does not + modify ``sys.stdout``, so ``sys.stdout.write()`` and ``print()`` + will still not support Unicode. + + .. versionchanged:: 4.0 + Added the ``color`` parameter. + + .. versionadded:: 3.0 + Added the ``err`` parameter. + + .. versionchanged:: 2.0 + Support colors on Windows if colorama is installed. + """ + if file is None: + if err: + file = _default_text_stderr() + else: + file = _default_text_stdout() + + # Convert non bytes/text into the native string type. + if message is not None and not isinstance(message, (str, bytes, bytearray)): + out: t.Optional[t.Union[str, bytes]] = str(message) + else: + out = message + + if nl: + out = out or "" + if isinstance(out, str): + out += "\n" + else: + out += b"\n" + + if not out: + file.flush() + return + + # If there is a message and the value looks like bytes, we manually + # need to find the binary stream and write the message in there. + # This is done separately so that most stream types will work as you + # would expect. Eg: you can write to StringIO for other cases. + if isinstance(out, (bytes, bytearray)): + binary_file = _find_binary_writer(file) + + if binary_file is not None: + file.flush() + binary_file.write(out) + binary_file.flush() + return + + # ANSI style code support. For no message or bytes, nothing happens. + # When outputting to a file instead of a terminal, strip codes. + else: + color = resolve_color_default(color) + + if should_strip_ansi(file, color): + out = strip_ansi(out) + elif WIN: + if auto_wrap_for_ansi is not None: + file = auto_wrap_for_ansi(file) # type: ignore + elif not color: + out = strip_ansi(out) + + file.write(out) # type: ignore + file.flush() + + +def get_binary_stream(name: "te.Literal['stdin', 'stdout', 'stderr']") -> t.BinaryIO: + """Returns a system stream for byte processing. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + """ + opener = binary_streams.get(name) + if opener is None: + raise TypeError(f"Unknown standard stream '{name}'") + return opener() + + +def get_text_stream( + name: "te.Literal['stdin', 'stdout', 'stderr']", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", +) -> t.TextIO: + """Returns a system stream for text processing. This usually returns + a wrapped stream around a binary stream returned from + :func:`get_binary_stream` but it also can take shortcuts for already + correctly configured streams. + + :param name: the name of the stream to open. Valid names are ``'stdin'``, + ``'stdout'`` and ``'stderr'`` + :param encoding: overrides the detected default encoding. + :param errors: overrides the default error mode. + """ + opener = text_streams.get(name) + if opener is None: + raise TypeError(f"Unknown standard stream '{name}'") + return opener(encoding, errors) + + +def open_file( + filename: str, + mode: str = "r", + encoding: t.Optional[str] = None, + errors: t.Optional[str] = "strict", + lazy: bool = False, + atomic: bool = False, +) -> t.IO: + """This is similar to how the :class:`File` works but for manual + usage. Files are opened non lazy by default. This can open regular + files as well as stdin/stdout if ``'-'`` is passed. + + If stdin/stdout is returned the stream is wrapped so that the context + manager will not close the stream accidentally. This makes it possible + to always use the function like this without having to worry to + accidentally close a standard stream:: + + with open_file(filename) as f: + ... + + .. versionadded:: 3.0 + + :param filename: the name of the file to open (or ``'-'`` for stdin/stdout). + :param mode: the mode in which to open the file. + :param encoding: the encoding to use. + :param errors: the error handling for this file. + :param lazy: can be flipped to true to open the file lazily. + :param atomic: in atomic mode writes go into a temporary file and it's + moved on close. + """ + if lazy: + return t.cast(t.IO, LazyFile(filename, mode, encoding, errors, atomic=atomic)) + f, should_close = open_stream(filename, mode, encoding, errors, atomic=atomic) + if not should_close: + f = t.cast(t.IO, KeepOpenFile(f)) + return f + + +def get_os_args() -> t.Sequence[str]: + """Returns the argument part of ``sys.argv``, removing the first + value which is the name of the script. + + .. deprecated:: 8.0 + Will be removed in Click 8.1. Access ``sys.argv[1:]`` directly + instead. + """ + import warnings + + warnings.warn( + "'get_os_args' is deprecated and will be removed in Click 8.1." + " Access 'sys.argv[1:]' directly instead.", + DeprecationWarning, + stacklevel=2, + ) + return sys.argv[1:] + + +def format_filename( + filename: t.Union[str, bytes, os.PathLike], shorten: bool = False +) -> str: + """Formats a filename for user display. The main purpose of this + function is to ensure that the filename can be displayed at all. This + will decode the filename to unicode if necessary in a way that it will + not fail. Optionally, it can shorten the filename to not include the + full path to the filename. + + :param filename: formats a filename for UI display. This will also convert + the filename into unicode without failing. + :param shorten: this optionally shortens the filename to strip of the + path that leads up to it. + """ + if shorten: + filename = os.path.basename(filename) + + return os.fsdecode(filename) + + +def get_app_dir(app_name: str, roaming: bool = True, force_posix: bool = False) -> str: + r"""Returns the config folder for the application. The default behavior + is to return whatever is most appropriate for the operating system. + + To give you an idea, for an app called ``"Foo Bar"``, something like + the following folders could be returned: + + Mac OS X: + ``~/Library/Application Support/Foo Bar`` + Mac OS X (POSIX): + ``~/.foo-bar`` + Unix: + ``~/.config/foo-bar`` + Unix (POSIX): + ``~/.foo-bar`` + Windows (roaming): + ``C:\Users\\AppData\Roaming\Foo Bar`` + Windows (not roaming): + ``C:\Users\\AppData\Local\Foo Bar`` + + .. versionadded:: 2.0 + + :param app_name: the application name. This should be properly capitalized + and can contain whitespace. + :param roaming: controls if the folder should be roaming or not on Windows. + Has no affect otherwise. + :param force_posix: if this is set to `True` then on any POSIX system the + folder will be stored in the home folder with a leading + dot instead of the XDG config home or darwin's + application support folder. + """ + if WIN: + key = "APPDATA" if roaming else "LOCALAPPDATA" + folder = os.environ.get(key) + if folder is None: + folder = os.path.expanduser("~") + return os.path.join(folder, app_name) + if force_posix: + return os.path.join(os.path.expanduser(f"~/.{_posixify(app_name)}")) + if sys.platform == "darwin": + return os.path.join( + os.path.expanduser("~/Library/Application Support"), app_name + ) + return os.path.join( + os.environ.get("XDG_CONFIG_HOME", os.path.expanduser("~/.config")), + _posixify(app_name), + ) + + +class PacifyFlushWrapper: + """This wrapper is used to catch and suppress BrokenPipeErrors resulting + from ``.flush()`` being called on broken pipe during the shutdown/final-GC + of the Python interpreter. Notably ``.flush()`` is always called on + ``sys.stdout`` and ``sys.stderr``. So as to have minimal impact on any + other cleanup code, and the case where the underlying file is not a broken + pipe, all calls and attributes are proxied. + """ + + def __init__(self, wrapped: t.IO) -> None: + self.wrapped = wrapped + + def flush(self) -> None: + try: + self.wrapped.flush() + except OSError as e: + import errno + + if e.errno != errno.EPIPE: + raise + + def __getattr__(self, attr: str) -> t.Any: + return getattr(self.wrapped, attr) + + +def _detect_program_name( + path: t.Optional[str] = None, _main: ModuleType = sys.modules["__main__"] +) -> str: + """Determine the command used to run the program, for use in help + text. If a file or entry point was executed, the file name is + returned. If ``python -m`` was used to execute a module or package, + ``python -m name`` is returned. + + This doesn't try to be too precise, the goal is to give a concise + name for help text. Files are only shown as their name without the + path. ``python`` is only shown for modules, and the full path to + ``sys.executable`` is not shown. + + :param path: The Python file being executed. Python puts this in + ``sys.argv[0]``, which is used by default. + :param _main: The ``__main__`` module. This should only be passed + during internal testing. + + .. versionadded:: 8.0 + Based on command args detection in the Werkzeug reloader. + + :meta private: + """ + if not path: + path = sys.argv[0] + + # The value of __package__ indicates how Python was called. It may + # not exist if a setuptools script is installed as an egg. It may be + # set incorrectly for entry points created with pip on Windows. + if getattr(_main, "__package__", None) is None or ( + os.name == "nt" + and _main.__package__ == "" + and not os.path.exists(path) + and os.path.exists(f"{path}.exe") + ): + # Executed a file, like "python app.py". + return os.path.basename(path) + + # Executed a module, like "python -m example". + # Rewritten by Python from "-m script" to "/path/to/script.py". + # Need to look at main module to determine how it was executed. + py_module = t.cast(str, _main.__package__) + name = os.path.splitext(os.path.basename(path))[0] + + # A submodule like "example.cli". + if name != "__main__": + py_module = f"{py_module}.{name}" + + return f"python -m {py_module.lstrip('.')}" + + +def _expand_args( + args: t.Iterable[str], + *, + user: bool = True, + env: bool = True, + glob_recursive: bool = True, +) -> t.List[str]: + """Simulate Unix shell expansion with Python functions. + + See :func:`glob.glob`, :func:`os.path.expanduser`, and + :func:`os.path.expandvars`. + + This intended for use on Windows, where the shell does not do any + expansion. It may not exactly match what a Unix shell would do. + + :param args: List of command line arguments to expand. + :param user: Expand user home directory. + :param env: Expand environment variables. + :param glob_recursive: ``**`` matches directories recursively. + + .. versionadded:: 8.0 + + :meta private: + """ + from glob import glob + + out = [] + + for arg in args: + if user: + arg = os.path.expanduser(arg) + + if env: + arg = os.path.expandvars(arg) + + matches = glob(arg, recursive=glob_recursive) + + if not matches: + out.append(arg) + else: + out.extend(matches) + + return out diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/INSTALLER b/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/INSTALLER new file mode 100644 index 000000000..a1b589e38 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/INSTALLER @@ -0,0 +1 @@ +pip diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/LICENSE b/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/LICENSE new file mode 100644 index 000000000..5f8fd634a --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/LICENSE @@ -0,0 +1,6 @@ +## License + +This work is dual-licensed under Apache 2.0 or BSD3. +You may select, at your option, one of the above-listed licenses. + +`SPDX-License-Identifier: Apache-2.0 OR BSD-3-Clause` diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/LICENSE.Apache b/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/LICENSE.Apache new file mode 100644 index 000000000..bff56b543 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/LICENSE.Apache @@ -0,0 +1,200 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2016 Datadog, Inc. + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/LICENSE.BSD3 b/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/LICENSE.BSD3 new file mode 100644 index 000000000..e8f3a81c1 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/LICENSE.BSD3 @@ -0,0 +1,24 @@ +Copyright (c) 2016, Datadog +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Datadog nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL DATADOG BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/METADATA b/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/METADATA new file mode 100644 index 000000000..dc280cc86 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/METADATA @@ -0,0 +1,59 @@ +Metadata-Version: 2.1 +Name: ddtrace +Version: 0.55.4 +Summary: Datadog tracing code +Home-page: https://github.com/DataDog/dd-trace-py +Author: Datadog, Inc. +Author-email: dev@datadoghq.com +License: BSD +Platform: UNKNOWN +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2.7 +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 :: 3.10 +Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* +Description-Content-Type: text/markdown +License-File: LICENSE +License-File: LICENSE.Apache +License-File: LICENSE.BSD3 +License-File: NOTICE +Requires-Dist: packaging (>=17.1) +Requires-Dist: tenacity (>=5) +Requires-Dist: attrs (>=19.2.0) +Requires-Dist: six (>=1.12.0) +Requires-Dist: enum34 ; python_version < "3.4" +Requires-Dist: typing ; python_version < "3.5" +Requires-Dist: protobuf (<3.18,>=3) ; python_version < "3.6" +Requires-Dist: pep562 ; python_version < "3.7" +Requires-Dist: funcsigs (>=1.0.0) ; python_version == "2.7" +Requires-Dist: protobuf (>=3) ; python_version >= "3.6" +Provides-Extra: opentracing +Requires-Dist: opentracing (>=2.0.0) ; extra == 'opentracing' + + +# dd-trace-py + +`ddtrace` is Datadog's tracing library for Python. It is used to trace requests +as they flow across web servers, databases and microservices so that developers +have great visibility into bottlenecks and troublesome requests. + +## Getting Started + +For a basic product overview, installation and quick start, check out our +[setup documentation][setup docs]. + +For more advanced usage and configuration, check out our [API +documentation][api docs]. + +For descriptions of terminology used in APM, take a look at the [official +documentation][visualization docs]. + +[setup docs]: https://docs.datadoghq.com/tracing/setup/python/ +[api docs]: https://ddtrace.readthedocs.io/ +[visualization docs]: https://docs.datadoghq.com/tracing/visualization/ + + diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/NOTICE b/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/NOTICE new file mode 100644 index 000000000..732c748d4 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/NOTICE @@ -0,0 +1,4 @@ +Datadog dd-trace-py +Copyright 2016-Present Datadog, Inc. + +This product includes software developed at Datadog, Inc. (https://www.datadoghq.com/). diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/RECORD b/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/RECORD new file mode 100644 index 000000000..0d5ca7c1e --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/RECORD @@ -0,0 +1,718 @@ +../../../bin/ddtrace-run,sha256=DCxg4zGUb2R-EqBb12RAYXqfDmfQmyfxpjQjrfzYIoo,256 +__pycache__/ddtrace_gevent_check.cpython-38.pyc,, +benchmarks/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +benchmarks/__pycache__/__init__.cpython-38.pyc,, +benchmarks/bm/__init__.py,sha256=kBMXCwudCv1V4w5NthjQKrPVwmgSCAQCx9hipfSfgcQ,102 +benchmarks/bm/__pycache__/__init__.cpython-38.pyc,, +benchmarks/bm/__pycache__/_scenario.cpython-38.pyc,, +benchmarks/bm/_scenario.py,sha256=_qB7EtQLr8LkNHbTSwSujMopa5cHMvQAG0iaBeYEqO8,2151 +ddtrace-0.55.4.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 +ddtrace-0.55.4.dist-info/LICENSE,sha256=OZvn-IQ0kjk6HmSP8Yi2WQqZBzGRJQmvBO5w9KBdD3o,186 +ddtrace-0.55.4.dist-info/LICENSE.Apache,sha256=5V2RruBHZQIcPyceiv51DjjvdvhgsgS4pnXAOHDuZkQ,11342 +ddtrace-0.55.4.dist-info/LICENSE.BSD3,sha256=J9S_Tq-hhvteDV2W8R0rqht5DZHkmvgdx3gnLZg4j6Q,1493 +ddtrace-0.55.4.dist-info/METADATA,sha256=XimVW6ZXuv5CVb5QgmbS2baIpQRt7zhv8LOgtgLUHyQ,2107 +ddtrace-0.55.4.dist-info/NOTICE,sha256=Wmf6iXVNfb58zWLK5pIkcbqMflb7pl38JhxjMwmjtyc,146 +ddtrace-0.55.4.dist-info/RECORD,, +ddtrace-0.55.4.dist-info/WHEEL,sha256=m4Wk6eOy92yJY87gVAkQnn-qnRb0vl_v2zsmx0hgXfo,109 +ddtrace-0.55.4.dist-info/entry_points.txt,sha256=p5eRDeO9gI2ranksxfzrevqMy7QEYPG5ZQBRNdkyoGk,219 +ddtrace-0.55.4.dist-info/top_level.txt,sha256=oo2f8zjoPEsC1AZq06GweHbkP6VzB8YWL56XtI2i_mQ,40 +ddtrace/__init__.py,sha256=UwhwpKWpnLVoWBy2gTbxY-iSrF80hcWEIsiHdSyhyKg,863 +ddtrace/__pycache__/__init__.cpython-38.pyc,, +ddtrace/__pycache__/_hooks.cpython-38.pyc,, +ddtrace/__pycache__/_version.cpython-38.pyc,, +ddtrace/__pycache__/compat.cpython-38.pyc,, +ddtrace/__pycache__/constants.cpython-38.pyc,, +ddtrace/__pycache__/context.cpython-38.pyc,, +ddtrace/__pycache__/encoding.cpython-38.pyc,, +ddtrace/__pycache__/filters.cpython-38.pyc,, +ddtrace/__pycache__/helpers.cpython-38.pyc,, +ddtrace/__pycache__/monkey.cpython-38.pyc,, +ddtrace/__pycache__/pin.cpython-38.pyc,, +ddtrace/__pycache__/provider.cpython-38.pyc,, +ddtrace/__pycache__/sampler.cpython-38.pyc,, +ddtrace/__pycache__/span.cpython-38.pyc,, +ddtrace/__pycache__/tracer.cpython-38.pyc,, +ddtrace/__pycache__/util.cpython-38.pyc,, +ddtrace/__pycache__/version.cpython-38.pyc,, +ddtrace/_hooks.py,sha256=vpsGwWoomIaHCy689rKM7Ozu3ypV8L8o3O1er3spOxI,3714 +ddtrace/_version.py,sha256=TsdkaNK0rNmQ2RKhnED2V1_DIEzYxpTCDe9G5M1q_Fc,144 +ddtrace/bootstrap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ddtrace/bootstrap/__pycache__/__init__.cpython-38.pyc,, +ddtrace/bootstrap/__pycache__/sitecustomize.cpython-38.pyc,, +ddtrace/bootstrap/sitecustomize.py,sha256=0B3XvRhria2JXUUeYVW4Et1_s6mLOBSiOq-tnl8HkKY,5474 +ddtrace/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ddtrace/commands/__pycache__/__init__.cpython-38.pyc,, +ddtrace/commands/__pycache__/ddtrace_run.cpython-38.pyc,, +ddtrace/commands/ddtrace_run.py,sha256=Z87dzzxQmYmp5WpCoxWSDpLRYG1ASXA33qPA_FJ3API,4461 +ddtrace/compat.py,sha256=4EWK_z7A9rVOSDc8HS7_4nkOV8hbNXq1mTIdFDDKB8o,2321 +ddtrace/constants.py,sha256=LqI4mir6JHcgaTEsXyGWK6tleGf9dp8zCqAa55uV56U,1350 +ddtrace/context.py,sha256=TpuKmScXVPtdonSqwiMRQokq_dELxK5-qm-SdVSG-1w,4155 +ddtrace/contrib/__init__.py,sha256=0SVwa5x1WzoPG2UvBhWXzMlX2OnDioEHjqHqgV1Yn2Q,152 +ddtrace/contrib/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/__pycache__/trace_utils.cpython-38.pyc,, +ddtrace/contrib/__pycache__/util.cpython-38.pyc,, +ddtrace/contrib/aiobotocore/__init__.py,sha256=nHsrBNpYSjcDjCSnqNKzlDzQgRew86V4BoDKqHr9yFg,873 +ddtrace/contrib/aiobotocore/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/aiobotocore/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/aiobotocore/patch.py,sha256=c5dTY7v_Yg-t5YwOLd63Ukd9ygVq72ryZSC7MgVOKFk,4834 +ddtrace/contrib/aiohttp/__init__.py,sha256=ZcRK_PQ4lRGvXBB2A_lCcWcwvk5mdb5rhgRHCgPW5aM,2251 +ddtrace/contrib/aiohttp/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/aiohttp/__pycache__/middlewares.cpython-38.pyc,, +ddtrace/contrib/aiohttp/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/aiohttp/__pycache__/template.cpython-38.pyc,, +ddtrace/contrib/aiohttp/middlewares.py,sha256=is2OvWJTLWPHqK-Cn_6Hroa0KYmLYnkz5iG9LmXzLx0,5262 +ddtrace/contrib/aiohttp/patch.py,sha256=IkvGlK18LrWisBESXVaV8aQUMyg-bwoTEQfp24-6FaI,1364 +ddtrace/contrib/aiohttp/template.py,sha256=iLd-CrDxItNz5bo1Qchu0E_tB8PFEhOSS24sxdYluOE,1024 +ddtrace/contrib/aiopg/__init__.py,sha256=6SzT90nwF01vdG6KSfWFr2iztrPWaLkxXl5EJpafFPU,764 +ddtrace/contrib/aiopg/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/aiopg/__pycache__/connection.cpython-38.pyc,, +ddtrace/contrib/aiopg/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/aiopg/connection.py,sha256=uaukQH7Q2T4WxVhfOcf4w6DGvJv36tiWJMldSxPabWc,3871 +ddtrace/contrib/aiopg/patch.py,sha256=LTFgz1PJhlFPSL_aJ9UVSsAfX3ZG1Jb-Jj7ifEvvGJY,1797 +ddtrace/contrib/algoliasearch/__init__.py,sha256=ETMHk79BfsJzESvck9pUNKVgpevjFx-U0vjaB8P53n8,900 +ddtrace/contrib/algoliasearch/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/algoliasearch/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/algoliasearch/patch.py,sha256=FyJTbGne96QoPJzLtSFi6CgkDOlGVb9bZnorMykhUrk,5072 +ddtrace/contrib/aredis/__init__.py,sha256=qx4q_fdbRyJC1wDppxSxXF1FYp3VFhmwKX-erwL0xck,1318 +ddtrace/contrib/aredis/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/aredis/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/aredis/patch.py,sha256=0VSlhmT0W8j4IrCa1WzgMefXgaJfFrNfQJGZ_hS1Ag4,4611 +ddtrace/contrib/asgi/__init__.py,sha256=EZ_uuOcQ5TvCzx6Xu7MYfs3npHeij9z9L94ygz-m5As,2081 +ddtrace/contrib/asgi/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/asgi/__pycache__/middleware.cpython-38.pyc,, +ddtrace/contrib/asgi/__pycache__/utils.cpython-38.pyc,, +ddtrace/contrib/asgi/middleware.py,sha256=KUADjD4OZv5HIJaQDQx3BD4MX6urSIQiA9O6R3-MUNk,5750 +ddtrace/contrib/asgi/utils.py,sha256=yjwwOejC2irGYD2B-ddoSfFLPKjXpouGKan4iAhjwdM,3273 +ddtrace/contrib/asyncio/__init__.py,sha256=VResycbg3PZkDFy8xCom3aL3dm_1ajC3-QifH3tGkGE,2459 +ddtrace/contrib/asyncio/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/asyncio/__pycache__/compat.cpython-38.pyc,, +ddtrace/contrib/asyncio/__pycache__/helpers.cpython-38.pyc,, +ddtrace/contrib/asyncio/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/asyncio/__pycache__/provider.cpython-38.pyc,, +ddtrace/contrib/asyncio/__pycache__/wrappers.cpython-38.pyc,, +ddtrace/contrib/asyncio/compat.py,sha256=ajEcZZcZyRJJX_QhqdC-43C-0p8sRQZMufDNyhYk9cQ,279 +ddtrace/contrib/asyncio/helpers.py,sha256=G1L7FBnpFVBkn5m9dEFHdXLG1ECY2LANpJAN2H3_nEU,3102 +ddtrace/contrib/asyncio/patch.py,sha256=CxpqQt5h27lo_DdoqLDJil27jFlj4lrb5zzJq-2d2H4,1336 +ddtrace/contrib/asyncio/provider.py,sha256=CIZMUeR3Glm-WrO7PQH0OUMUSUXjI-YMMv73zueWFpA,2855 +ddtrace/contrib/asyncio/wrappers.py,sha256=svG3-7JQ9kM1pFcKYCji-LRGGqe5ZDUTFx4h3qkDpsM,953 +ddtrace/contrib/boto/__init__.py,sha256=QnyFSOLycdIeslkdzLODYjPtfnSgaX2ZU_IfVtOBE2E,684 +ddtrace/contrib/boto/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/boto/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/boto/patch.py,sha256=_28Z946Hw1XVHVZ0pAVL0H_XLMAK9K1J0eVJvhg0b9w,5738 +ddtrace/contrib/botocore/__init__.py,sha256=s0HTe9a7xGdnXljlyxxCmhbDVE_Od5_AzThLk_vseJ4,1560 +ddtrace/contrib/botocore/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/botocore/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/botocore/patch.py,sha256=nJfnOePGxqxn0srmXTQmzZFqZaKn6-eZFw2Ma4R8m14,6297 +ddtrace/contrib/bottle/__init__.py,sha256=pSvNJNq_L7Pp45SPiZLBR1etlZ4SSFu0RwTdbJBg8I0,1077 +ddtrace/contrib/bottle/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/bottle/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/bottle/__pycache__/trace.cpython-38.pyc,, +ddtrace/contrib/bottle/patch.py,sha256=zw6g6t7wseaTZhmbx2bH_GgSRVIdADa9VQFYuKbt8tQ,798 +ddtrace/contrib/bottle/trace.py,sha256=0xcuHbBL1vqLXKZeZXbUEauZgATCQ7zG7SzK9jW2l5g,3574 +ddtrace/contrib/cassandra/__init__.py,sha256=KyK-ud5RY4uJQLCQmR488TSpw9VjakMaeF5J0Uh-9Dw,1226 +ddtrace/contrib/cassandra/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/cassandra/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/cassandra/__pycache__/session.cpython-38.pyc,, +ddtrace/contrib/cassandra/patch.py,sha256=gTEULNQXWMvEH4VQHcvBthVH3XGWytnHLMCum2XcjeQ,89 +ddtrace/contrib/cassandra/session.py,sha256=9qbmQE4zTDCOz80ahQw6gDL-_gUSigVUaf2288fwrqA,9976 +ddtrace/contrib/celery/__init__.py,sha256=tPXuqw3E3Id4lPG4fq_CZWk4LbTBUT_72aEiEdedIFY,1709 +ddtrace/contrib/celery/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/celery/__pycache__/app.cpython-38.pyc,, +ddtrace/contrib/celery/__pycache__/constants.cpython-38.pyc,, +ddtrace/contrib/celery/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/celery/__pycache__/signals.cpython-38.pyc,, +ddtrace/contrib/celery/__pycache__/task.cpython-38.pyc,, +ddtrace/contrib/celery/__pycache__/utils.cpython-38.pyc,, +ddtrace/contrib/celery/app.py,sha256=Bmlt6O5jcyvJV6J607_cmGct_M1-QEYqA0lju4GlQmA,1884 +ddtrace/contrib/celery/constants.py,sha256=hkqiuzQQir6nuyiGL1WMqbFYT_5qYwdbLeqkFxCTa7E,485 +ddtrace/contrib/celery/patch.py,sha256=Skmxw_pFyKwzwwv_2uUrYw2qL-IPsT_9VftA2K4JR3c,1091 +ddtrace/contrib/celery/signals.py,sha256=1LLp__uA9uvYoCQKxDpq-dXy_muujJIzCYZ1geTakjA,7082 +ddtrace/contrib/celery/task.py,sha256=Nr0EuNA_LeO-H9ad0YFiRusk0QcGIbd4v6-AQ6PgBOM,926 +ddtrace/contrib/celery/utils.py,sha256=0GHU1bi9jC4uH6m9M63SOc5WprnOUaVGNePhkmK7vHU,4291 +ddtrace/contrib/cherrypy/__init__.py,sha256=Uzip8JhUAylwcAI2HOuWj8X-mURKzNXcNdk3Ag7_0p4,1562 +ddtrace/contrib/cherrypy/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/cherrypy/__pycache__/middleware.cpython-38.pyc,, +ddtrace/contrib/cherrypy/middleware.py,sha256=U4RWepJa-uEHfnicibJ_UNJhk8km6e6aeOa57-OIWGo,5061 +ddtrace/contrib/consul/__init__.py,sha256=SY3yeLgOQrlNCY46IWax2U4581dY5ewQu_4QBBfmong,834 +ddtrace/contrib/consul/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/consul/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/consul/patch.py,sha256=v6nZ7fIZJLqQJEJTOdqr0EnUJSEU2e2C1fWS4XhLlpo,1859 +ddtrace/contrib/dbapi/__init__.py,sha256=2QQiIPsHDtln4b330or6Wym5xvwFqQjRi2dGY0Slgzk,10940 +ddtrace/contrib/dbapi/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/django/__init__.py,sha256=ieA39ryf9_YNKvejV_BKmHxV-2v85eP9bVKMPerCkhc,4357 +ddtrace/contrib/django/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/django/__pycache__/_asgi.cpython-38.pyc,, +ddtrace/contrib/django/__pycache__/compat.cpython-38.pyc,, +ddtrace/contrib/django/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/django/__pycache__/restframework.cpython-38.pyc,, +ddtrace/contrib/django/__pycache__/utils.cpython-38.pyc,, +ddtrace/contrib/django/_asgi.py,sha256=ZvFX2eM4A6aZb5ngLwzeVB_WXkPvzb918u1ls1iwLzc,1381 +ddtrace/contrib/django/compat.py,sha256=vooC381G_q8Y8d9gakb1UTdvrXBLo2s7VuAq--UFEjI,909 +ddtrace/contrib/django/patch.py,sha256=VVsUMOTERjnW3XYRGU3ipsrOsMgX-nv5Pofp1P_gLno,21419 +ddtrace/contrib/django/restframework.py,sha256=uWiWt9D58NGLtDtjlZBBiIA-B9KogQjRTVq7yjTECk8,1159 +ddtrace/contrib/django/utils.py,sha256=aXHPvEHwTe2T8XwU0pZHpHyC-dO4iEP0E07x_o3w8PM,11308 +ddtrace/contrib/dogpile_cache/__init__.py,sha256=Wmx5yn7DPO61IsgbZGjFV4jVzoCy_iSDdjoIt9vUmec,1590 +ddtrace/contrib/dogpile_cache/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/dogpile_cache/__pycache__/lock.cpython-38.pyc,, +ddtrace/contrib/dogpile_cache/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/dogpile_cache/__pycache__/region.cpython-38.pyc,, +ddtrace/contrib/dogpile_cache/lock.py,sha256=KsNQ8vBFAAg-8s9SMXjiNFbKALCMWEVhnBd4ObD0clc,1627 +ddtrace/contrib/dogpile_cache/patch.py,sha256=X_l14SqEq6yghMBH5fbK4tZ0hdqDVLImsolLTlrfOEE,1550 +ddtrace/contrib/dogpile_cache/region.py,sha256=YSWL4qdBpz2pwqtkBF9EfOpvL5CWjwz8j8jV2Nrb73U,1279 +ddtrace/contrib/elasticsearch/__init__.py,sha256=Zhyn6H4XIPysGbd7Q8pW9ZJ9MpGb7sg0M0-W_WIPZnM,815 +ddtrace/contrib/elasticsearch/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/elasticsearch/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/elasticsearch/__pycache__/quantize.cpython-38.pyc,, +ddtrace/contrib/elasticsearch/patch.py,sha256=OdB5Pzqq7Uv-6Y83Ce-VfYeierUnDiRw50u4ASu_t6E,3930 +ddtrace/contrib/elasticsearch/quantize.py,sha256=wdiGa9N2CcPXw614lwb6-kCTBrxvkubtmb4KI2S9H5c,1052 +ddtrace/contrib/falcon/__init__.py,sha256=kPO-5D436j8mKChnXV2yqsfD3zAH5rVtSmASksl2qKQ,1457 +ddtrace/contrib/falcon/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/falcon/__pycache__/middleware.cpython-38.pyc,, +ddtrace/contrib/falcon/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/falcon/middleware.py,sha256=bK1PtI9mdUuHJAC656LQWTSnuqcfJhBUgqpQIR0xkIQ,3796 +ddtrace/contrib/falcon/patch.py,sha256=c7qLMRpg5WUMXq2HiRkFwsazIDlAATTQ7n3tUtTAkYs,1145 +ddtrace/contrib/fastapi/__init__.py,sha256=B_AwffCU3WMUb0Y4fyPPlwXdyxbAdSzoFDQDbrOnanc,1604 +ddtrace/contrib/fastapi/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/fastapi/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/fastapi/patch.py,sha256=0mqiMyNVlwmWjSOn_Eb251NH13qUw3dQDO6R37xXdqs,1418 +ddtrace/contrib/flask/__init__.py,sha256=Qqq0SpucjfPR-qrhLNJJ42H7bVtBx6iWXd91krF5lRg,2330 +ddtrace/contrib/flask/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/flask/__pycache__/helpers.cpython-38.pyc,, +ddtrace/contrib/flask/__pycache__/middleware.cpython-38.pyc,, +ddtrace/contrib/flask/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/flask/__pycache__/wrappers.cpython-38.pyc,, +ddtrace/contrib/flask/helpers.py,sha256=e6bWhOPsqSjlRkSsMoxfcNyGTRMqRQUebYkiVtPGr_w,1088 +ddtrace/contrib/flask/middleware.py,sha256=9BBGncZf9T197BGj98uXJzzAG6UU_T3BqyEjZ3YLgFU,8571 +ddtrace/contrib/flask/patch.py,sha256=ciu70oD0VqwBpGAiNFe-xf3s0StvznlJft0Cy5xkXjw,19164 +ddtrace/contrib/flask/wrappers.py,sha256=-UuUp3_rkTjPWseztsKk-IyUb47sQ_EPHJ6ZS1T70mc,1537 +ddtrace/contrib/flask_cache/__init__.py,sha256=z7ajIF6vddKAUl0KDTYSpbFnpSEiiBtJnhiTVhqCL10,1637 +ddtrace/contrib/flask_cache/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/flask_cache/__pycache__/tracers.cpython-38.pyc,, +ddtrace/contrib/flask_cache/__pycache__/utils.cpython-38.pyc,, +ddtrace/contrib/flask_cache/tracers.py,sha256=HXoXJVpNw0FDZrbxWFS2A31gvCRD2yXqHkTKpk9umYc,5796 +ddtrace/contrib/flask_cache/utils.py,sha256=bhCIB8lu7ov6bfvTa-J_u-elnevQM4_iAZaohHV1eqI,1936 +ddtrace/contrib/futures/__init__.py,sha256=6nUV3-p49uKvkUNgKtNKdm2GKTxrkrtVeyQgcgO0gWg,821 +ddtrace/contrib/futures/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/futures/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/futures/__pycache__/threading.cpython-38.pyc,, +ddtrace/contrib/futures/patch.py,sha256=WbGoW88ASbHGQRk9IyDPYxAIkmuCAR0mQNEQpct4fbQ,662 +ddtrace/contrib/futures/threading.py,sha256=pMA0E9akmNnE8JJDCsgWJXVFop-fg5FEidkC-fkn0p8,1722 +ddtrace/contrib/gevent/__init__.py,sha256=94AzepjMNJa5E9nKNNcLllfaos0LrYkIAMfcJkEcttA,2126 +ddtrace/contrib/gevent/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/gevent/__pycache__/greenlet.cpython-38.pyc,, +ddtrace/contrib/gevent/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/gevent/__pycache__/provider.cpython-38.pyc,, +ddtrace/contrib/gevent/greenlet.py,sha256=rEzvw7pibHIXNe8tOK4Jm9J7Cb4pK6_nt-me8p6lNVE,2241 +ddtrace/contrib/gevent/patch.py,sha256=_ximL6YK_1NVIBmzgF_vIBpCQ4yG1M4RqddmBMmMC6c,2244 +ddtrace/contrib/gevent/provider.py,sha256=wWh_EnT4xdpekgdJhiqGWnTj2CXyUfLlYJAVYX2WrjA,1468 +ddtrace/contrib/grpc/__init__.py,sha256=bO2KTcKKrZCoI35dx69kbKlsMgBbz-xlYMnOrGmSIXI,2166 +ddtrace/contrib/grpc/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/grpc/__pycache__/client_interceptor.cpython-38.pyc,, +ddtrace/contrib/grpc/__pycache__/constants.cpython-38.pyc,, +ddtrace/contrib/grpc/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/grpc/__pycache__/server_interceptor.cpython-38.pyc,, +ddtrace/contrib/grpc/__pycache__/utils.cpython-38.pyc,, +ddtrace/contrib/grpc/client_interceptor.py,sha256=uYbYHBqFptvg1iCrHLVOI1NjSZjywkZx6D65dKkp34w,10297 +ddtrace/contrib/grpc/constants.py,sha256=9ii-eBuPm8UzuXJQGzeoxPVQNHbA9pMxeFDIiSUvotg,908 +ddtrace/contrib/grpc/patch.py,sha256=2b8Rtd5ZIlzYe4cjHcPXrptPt_WV1-Gpa9AuBlEpxhs,3391 +ddtrace/contrib/grpc/server_interceptor.py,sha256=vtOXvAs5yQDtHF64eVCORkOz9PO6wN552IiSs5LH0u0,4664 +ddtrace/contrib/grpc/utils.py,sha256=U67Ijho-_W2V11oHEPOY7nrOMqjOzGmXXL_GB9MNRRY,2509 +ddtrace/contrib/httplib/__init__.py,sha256=W_A6MCkb438Z95IT3VntRU7uVMDWE8ZPTpiPxZcfLvI,1506 +ddtrace/contrib/httplib/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/httplib/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/httplib/patch.py,sha256=PSPuqhg2SGHgODxl5sdr6sEb5V1uRemBQnhaxhOySNM,7065 +ddtrace/contrib/httpx/__init__.py,sha256=Ix0IbVW7W-7C77QtzXD1CdZ6XUr0UfS23XOfqEwG9p0,2498 +ddtrace/contrib/httpx/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/httpx/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/httpx/patch.py,sha256=O-1fjyX166k-wsfkyrsRV5qa8i2mr7_xMd_GafBnHAA,4644 +ddtrace/contrib/jinja2/__init__.py,sha256=OUQCQku8w5Cl2CzkwY5wtbcFdDUfOCXRAABpPzi9K-A,1245 +ddtrace/contrib/jinja2/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/jinja2/__pycache__/constants.cpython-38.pyc,, +ddtrace/contrib/jinja2/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/jinja2/constants.py,sha256=BpRqO-bQIWzrHFO7DtJAZ4v7lH7HpYWjYri7yCOG-Ts,35 +ddtrace/contrib/jinja2/patch.py,sha256=csTFVcvuK9H4AS6V33izdHYYhe-giXQg6KlKiUGDZyI,3214 +ddtrace/contrib/kombu/__init__.py,sha256=9dkzHdyCC7-X_V58IUalMD_9tGG5_nCcpwfnbUre-7g,1608 +ddtrace/contrib/kombu/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/kombu/__pycache__/constants.cpython-38.pyc,, +ddtrace/contrib/kombu/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/kombu/__pycache__/utils.cpython-38.pyc,, +ddtrace/contrib/kombu/constants.py,sha256=RhhLOyKsbyFLnko4wvuyDQvBv_wy0CMN27k4akMSlak,26 +ddtrace/contrib/kombu/patch.py,sha256=gYEHxPMPcd8rEpAccpmCOxqAvnnsvjVF86sfqeqAquQ,4321 +ddtrace/contrib/kombu/utils.py,sha256=xjAzg2zr7o74Iy7Bc2vIQvfvm_W82ZzvbiBHLNtCa7E,1101 +ddtrace/contrib/logging/__init__.py,sha256=n99351HcYyMUL6ZFysiJdvFcg499vO9klZKs1PQfk5E,2166 +ddtrace/contrib/logging/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/logging/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/logging/patch.py,sha256=FzFAFgLvJGtOx2Z1OboiYJeuOWXGR6il6TUUK51kGbc,4320 +ddtrace/contrib/mako/__init__.py,sha256=s2C8nifhGMO1O5mCP4BTx7EOMYLUOfmiwkgljDw3IFI,588 +ddtrace/contrib/mako/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/mako/__pycache__/constants.cpython-38.pyc,, +ddtrace/contrib/mako/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/mako/constants.py,sha256=BpRqO-bQIWzrHFO7DtJAZ4v7lH7HpYWjYri7yCOG-Ts,35 +ddtrace/contrib/mako/patch.py,sha256=0pNus4f2KjbKj9vCk-UCK5PrVoayATbRx1ry_yDaj8I,2080 +ddtrace/contrib/mariadb/__init__.py,sha256=PXwfQKozHsQNq6rh2Px_w-xHw8XQnmsr27RmRtnnadU,1634 +ddtrace/contrib/mariadb/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/mariadb/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/mariadb/patch.py,sha256=RkSagzkI_qDd5JjpT_KH596ZHLOCoa3na14npYK_6qU,1250 +ddtrace/contrib/molten/__init__.py,sha256=_MGjAFUuzSFbtX30vNhw4mmU5sLMPMFpjoqj0KvJTHo,1268 +ddtrace/contrib/molten/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/molten/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/molten/__pycache__/wrappers.cpython-38.pyc,, +ddtrace/contrib/molten/patch.py,sha256=FsBkLJHcego8o2z3HuxsNRX1J9mEzSAUt62qAt86ZyM,5225 +ddtrace/contrib/molten/wrappers.py,sha256=VdJfn6waJZK1-d4XT0JWMl-iBqoVi8PJNQ2XmC0y1eU,3312 +ddtrace/contrib/mongoengine/__init__.py,sha256=Bk5uz8reJ2fRE5FTrQG4j2V_G9zQrlLgNvArlw5PBZ8,890 +ddtrace/contrib/mongoengine/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/mongoengine/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/mongoengine/__pycache__/trace.cpython-38.pyc,, +ddtrace/contrib/mongoengine/patch.py,sha256=0EnBafNG834UUGqUlp5PEfU8pjSs2Z2rM--JJl_9GmI,438 +ddtrace/contrib/mongoengine/trace.py,sha256=cMIIt5Dp8yWM0kmIEys-31wPXPQKlf5WwcqmdX8iF7U,1170 +ddtrace/contrib/mysql/__init__.py,sha256=fwelUWtASBH613-O0L3yKkwGtOyt0mkOhdX6E2yox9I,2076 +ddtrace/contrib/mysql/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/mysql/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/mysql/__pycache__/tracers.cpython-38.pyc,, +ddtrace/contrib/mysql/patch.py,sha256=QEO3EX9mdeHEqtg01-Vz0mjth1Cv9AKKeuHqi5Pc36k,1512 +ddtrace/contrib/mysql/tracers.py,sha256=xXVri5QBHcAJGRBzq8aQ6m8r7jauaxNU0jWlqK4H-VU,240 +ddtrace/contrib/mysqldb/__init__.py,sha256=SpGcfRxVB0mqu_q3z5NV-aoVdsL51z9dizPt7h-7B38,1893 +ddtrace/contrib/mysqldb/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/mysqldb/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/mysqldb/patch.py,sha256=WSTdttV6k-A4IajuuxzQDG6RbTXCcmXfP0sWSBrT8bg,1920 +ddtrace/contrib/psycopg/__init__.py,sha256=CKkYbGpZGDAl8Ig7O07CYS5WzYxAnlBN_gOsNzhuc60,1613 +ddtrace/contrib/psycopg/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/psycopg/__pycache__/connection.cpython-38.pyc,, +ddtrace/contrib/psycopg/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/psycopg/connection.py,sha256=hv8nR0ZkFMbOHrmd-F-rMlq90kzqYAm7MWFhX9gevFo,3283 +ddtrace/contrib/psycopg/patch.py,sha256=93fT6GCpOIsozqV6l3tOev1pTMQDgYCTqjE1GmPgLpI,6201 +ddtrace/contrib/pylibmc/__init__.py,sha256=wbT3awnMVI67TAQiJcg1_amnY9cLowvoqYZloD2ADtA,944 +ddtrace/contrib/pylibmc/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/pylibmc/__pycache__/addrs.cpython-38.pyc,, +ddtrace/contrib/pylibmc/__pycache__/client.cpython-38.pyc,, +ddtrace/contrib/pylibmc/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/pylibmc/addrs.py,sha256=S1wHEMy_T_zZWom_p6_u9eyC1W39nn_sXE9gC9nsTGg,365 +ddtrace/contrib/pylibmc/client.py,sha256=7m60ghYKqT4FCz4gSV65R0m696eElsdfclgMO7TWscE,5561 +ddtrace/contrib/pylibmc/patch.py,sha256=7r-aJQULmY8A7JGvcOcWk1bOnMvcRJOdLNbAFRkxcLU,217 +ddtrace/contrib/pylons/__init__.py,sha256=Dm_uea1ZTslO5yiYTfB0OuOQDvosl-jHR44NXvsQYEA,1660 +ddtrace/contrib/pylons/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/pylons/__pycache__/compat.cpython-38.pyc,, +ddtrace/contrib/pylons/__pycache__/constants.cpython-38.pyc,, +ddtrace/contrib/pylons/__pycache__/middleware.cpython-38.pyc,, +ddtrace/contrib/pylons/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/pylons/__pycache__/renderer.cpython-38.pyc,, +ddtrace/contrib/pylons/compat.py,sha256=yDoHyo3R7FNYpqCoOOz4HxTNhfj8TpMJW5Jqs64gUMw,174 +ddtrace/contrib/pylons/constants.py,sha256=BQOdVHL_aXDLZpjE7q4XFCXxo0aAe79HqltMudK5HNY,43 +ddtrace/contrib/pylons/middleware.py,sha256=ImH2DGKet9WLbtVOjceRRniEuJKDIw0i1twsRXXcVkg,4889 +ddtrace/contrib/pylons/patch.py,sha256=twvUNWOiGJZYdAO9XKOYn53fJ_9qvW9hspmvAI7gSHA,1457 +ddtrace/contrib/pylons/renderer.py,sha256=ro_4C8noWFRHuAheM5dQz-MPfDp8rW2hUy12rQtnIvQ,1268 +ddtrace/contrib/pymemcache/__init__.py,sha256=kfbpc_SFdnw7DE929sYm0bI2sUxJefz4vcRt_mIseQo,1150 +ddtrace/contrib/pymemcache/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/pymemcache/__pycache__/client.cpython-38.pyc,, +ddtrace/contrib/pymemcache/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/pymemcache/client.py,sha256=Bd3MxhoyC3Winl73VK1EhA5W1Kw8ZjC80Xkm7kprKbM,6801 +ddtrace/contrib/pymemcache/patch.py,sha256=A3hEoAG11TxtnERDGbZbqgwg-vPu7lB-j_J6_k_hgdw,1609 +ddtrace/contrib/pymongo/__init__.py,sha256=YpUSCktAoFL_M7EuyNhRTJE70AX2XL4FETecntAcbfM,1231 +ddtrace/contrib/pymongo/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/pymongo/__pycache__/client.cpython-38.pyc,, +ddtrace/contrib/pymongo/__pycache__/parse.cpython-38.pyc,, +ddtrace/contrib/pymongo/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/pymongo/client.py,sha256=kFOl8-WKgQ9GJSTCuUr2YTZqzGpRYhyiucmu3aZTxo8,9548 +ddtrace/contrib/pymongo/parse.py,sha256=kJccHF7TKlWnfrN75duPAkRQeKph75h3LyzON0y_cBs,6292 +ddtrace/contrib/pymongo/patch.py,sha256=seZrRnJbukNK_WPsbW8tQ3rQgg_UjaiPPM_th0YBjso,2405 +ddtrace/contrib/pymysql/__init__.py,sha256=IloQB5S4PKR7hY4iVoItfoTzKqvfA-wj3x103oQZbqQ,1684 +ddtrace/contrib/pymysql/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/pymysql/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/pymysql/__pycache__/tracers.cpython-38.pyc,, +ddtrace/contrib/pymysql/patch.py,sha256=Sl8gy_3NXFAfuZXZ0d7SA7sDbva3jOUjBVCcQRMTPxs,1226 +ddtrace/contrib/pymysql/tracers.py,sha256=tAy6B-VAJywrhPdBR4esB9gHKbhhXSQ9XgRSgTtW6DA,245 +ddtrace/contrib/pynamodb/__init__.py,sha256=3xCQXBLKhLH3ER1CmrryRf227U_TzBCG8WYHJ0YSAcw,937 +ddtrace/contrib/pynamodb/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/pynamodb/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/pynamodb/patch.py,sha256=7eEir_rwjZbDhBKEFvP9udy_wqW_BlXwooGCNzHbZsk,2587 +ddtrace/contrib/pyodbc/__init__.py,sha256=VfyetYAFAcKfpsNOtKZxtXaBarQzPb-M5D-B-U3lHbk,1522 +ddtrace/contrib/pyodbc/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/pyodbc/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/pyodbc/patch.py,sha256=FMBGx1xRVDW_EqPh1VAtdotzLs_FIwp2Gk7zw7uU57M,1350 +ddtrace/contrib/pyramid/__init__.py,sha256=jLvdWS3hef7m2Uyi_QbLpXATxdJXJLioSUZ5EaHtCxo,1706 +ddtrace/contrib/pyramid/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/pyramid/__pycache__/constants.cpython-38.pyc,, +ddtrace/contrib/pyramid/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/pyramid/__pycache__/trace.cpython-38.pyc,, +ddtrace/contrib/pyramid/constants.py,sha256=69TDS3jZ0kf6M7WlNT60Nv4qOWIiGEcbsCeG8_Qp9vw,310 +ddtrace/contrib/pyramid/patch.py,sha256=EvirLmtpwptjZZyzOVNj8oJbwQGNMDCqcONDs1IXT2Q,3523 +ddtrace/contrib/pyramid/trace.py,sha256=gMfnmhQ7HEqXos1ln17ewR_rM69brlVpx-ADPK7IXLI,4950 +ddtrace/contrib/pytest/__init__.py,sha256=IYqgSShVB6_Peu_hcMrQZxRrlJa45mKU3XplNxQa5e8,1381 +ddtrace/contrib/pytest/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/pytest/__pycache__/constants.cpython-38.pyc,, +ddtrace/contrib/pytest/__pycache__/plugin.cpython-38.pyc,, +ddtrace/contrib/pytest/constants.py,sha256=g0fRwf78PvNSr8HKd2Cuv7sDjGZo_IcM6jsrfvk1P5E,85 +ddtrace/contrib/pytest/plugin.py,sha256=00vrONmqM-CWlLUKtQv2L6emPmatSxSFBsWeaDTIjZo,7298 +ddtrace/contrib/redis/__init__.py,sha256=nBzvVA8tNXXjLWWYIn--bz3jVW8c-W-f4jan0uElpwk,1413 +ddtrace/contrib/redis/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/redis/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/redis/__pycache__/tracers.cpython-38.pyc,, +ddtrace/contrib/redis/__pycache__/util.cpython-38.pyc,, +ddtrace/contrib/redis/patch.py,sha256=Jtye0b_560Q79fb3_Fclta6xNyk_qCF0Re6lnQzqzDQ,4248 +ddtrace/contrib/redis/tracers.py,sha256=7Cg45xIuanDQA_loi_uPPjA28KGNbeNCkDSRuS6Y6As,627 +ddtrace/contrib/redis/util.py,sha256=rWVbittga1H-1MADSNm_YE61C7w-UCk9m0OTR9VTDkc,1350 +ddtrace/contrib/rediscluster/__init__.py,sha256=I0BnwxwhzWOWjBBFFVgrOfqso0mz43VUAgHjxXm4eAA,846 +ddtrace/contrib/rediscluster/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/rediscluster/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/rediscluster/patch.py,sha256=iG-e7BP7b44qmDEStXuxeWVE1KWk0HDMonsNuUPCAIo,3068 +ddtrace/contrib/requests/__init__.py,sha256=ZJrpCXrjOUUS5eqGxL1fdZuCdb-bnavbgab50ybTT-w,2200 +ddtrace/contrib/requests/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/requests/__pycache__/connection.cpython-38.pyc,, +ddtrace/contrib/requests/__pycache__/constants.cpython-38.pyc,, +ddtrace/contrib/requests/__pycache__/legacy.cpython-38.pyc,, +ddtrace/contrib/requests/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/requests/__pycache__/session.cpython-38.pyc,, +ddtrace/contrib/requests/connection.py,sha256=23LmLon9Kw-_bm3FBVGNEYazmLY1tov0wnCO2MGuDk4,3824 +ddtrace/contrib/requests/constants.py,sha256=HY71FxxxJKkCVoCNspOczEf9Ji6QcVDTeDCgYyuA0qU,29 +ddtrace/contrib/requests/legacy.py,sha256=PBJnJWYqYnknrb4z3nHzFyrNjT98520dtP3dNE8vt2k,1096 +ddtrace/contrib/requests/patch.py,sha256=tFTEkMMKHBgujSXUUAJMgwmL3VT46WGdE4h4mD4oEw4,1499 +ddtrace/contrib/requests/session.py,sha256=_vtXsnkadx3J6cJXjDcz39VKPQIzMVLWf0XyB-hOXm8,512 +ddtrace/contrib/rq/__init__.py,sha256=DiPFFDtb6uMNMRxWUh-r_R86g5TDdrdm5iueHE8qkcY,7169 +ddtrace/contrib/rq/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/sanic/__init__.py,sha256=cpw9fqVoCWWvOOqm83EFP-Mh4yR-SmiPYGK38CsgVRw,1769 +ddtrace/contrib/sanic/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/sanic/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/sanic/patch.py,sha256=o-aDW78nxgt0PFExL4E1YcpZsSIHWKzX-7aGwQwsbaE,5825 +ddtrace/contrib/snowflake/__init__.py,sha256=LAVyxarL6ZrWZ0vEduhT-SAd061j39_Ye-7dGJ2ThnM,1834 +ddtrace/contrib/snowflake/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/snowflake/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/snowflake/patch.py,sha256=yOixIjVZCrppxPe_W7am9VYH6szMVC36pLxuUuyx5N4,1686 +ddtrace/contrib/sqlalchemy/__init__.py,sha256=Eiet6PvnV-cmFWACE2mud6PWTl-gA8FJsjeVMd8hiQo,906 +ddtrace/contrib/sqlalchemy/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/sqlalchemy/__pycache__/engine.cpython-38.pyc,, +ddtrace/contrib/sqlalchemy/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/sqlalchemy/engine.py,sha256=Ok_4YYJtronT2AZtIqe6EGPWiAGFR_JMl6vmibpiZas,4945 +ddtrace/contrib/sqlalchemy/patch.py,sha256=Kc2Iawu6Lt2kzjZSj5jC9wAYCke24KUMgI66X4xvtkU,731 +ddtrace/contrib/sqlite3/__init__.py,sha256=MmNK2B_LRgtG3GFvBDCNi4pnHQKuiwkIUaQhr_MRdfo,1404 +ddtrace/contrib/sqlite3/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/sqlite3/__pycache__/connection.cpython-38.pyc,, +ddtrace/contrib/sqlite3/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/sqlite3/connection.py,sha256=7FLn4TPtC5Wfrt-34uxjiq9QJd9pQEumcy6C-7XuqPw,218 +ddtrace/contrib/sqlite3/patch.py,sha256=eOffvuER_PiTLIlEjwngfzFxDqK39pybNowM4m5AV28,2122 +ddtrace/contrib/starlette/__init__.py,sha256=l4JthS_Z0AczkwQD0g3gB2crgCGB48n48bmhLf0ZPT8,2239 +ddtrace/contrib/starlette/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/starlette/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/starlette/patch.py,sha256=pETjY-b0lCC70iRr1cKpjVNo3KbQoR2ONAMvVdTjsjg,1750 +ddtrace/contrib/tornado/__init__.py,sha256=5_aD300_AdzGCIa9jvuRq0_OFSRTuctb7GaqauBMz2E,4530 +ddtrace/contrib/tornado/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/tornado/__pycache__/application.cpython-38.pyc,, +ddtrace/contrib/tornado/__pycache__/compat.cpython-38.pyc,, +ddtrace/contrib/tornado/__pycache__/constants.cpython-38.pyc,, +ddtrace/contrib/tornado/__pycache__/decorators.cpython-38.pyc,, +ddtrace/contrib/tornado/__pycache__/handlers.cpython-38.pyc,, +ddtrace/contrib/tornado/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/tornado/__pycache__/stack_context.cpython-38.pyc,, +ddtrace/contrib/tornado/__pycache__/template.cpython-38.pyc,, +ddtrace/contrib/tornado/application.py,sha256=Ck3kofSMHPzr68Brrl9i0OKUAAgsJlqdm2VFEfy-MWc,1773 +ddtrace/contrib/tornado/compat.py,sha256=aujAgqKEOXeL3XuZKHFcijlEWVW-Wv20d2pPeCX1ayY,360 +ddtrace/contrib/tornado/constants.py,sha256=PsZUkpzC0uUfshu3QrhvPMj5b669K9w1pXGHgitaK00,205 +ddtrace/contrib/tornado/decorators.py,sha256=N859tWDEDwBAH-mSdAD7SJ-P0xx6MfY1e2oKNZLXAyc,3225 +ddtrace/contrib/tornado/handlers.py,sha256=Fjc20khMtgSk4XdfDCOwQHtF5m_askOWg9t6L6Bo6DA,4272 +ddtrace/contrib/tornado/patch.py,sha256=JzhMRht6qYnp8O_X0onx0Mh8SkzkWVFmmgKTjf3Ca8Y,2111 +ddtrace/contrib/tornado/stack_context.py,sha256=BwYFLRN_H2FoTWVqRumLEC1vWbOF0bEPTyqoWGh79Zc,6113 +ddtrace/contrib/tornado/template.py,sha256=4oE6gnM7YrLtbsMsdP8xmRHGLIfLkB08uTJiynJ1ouA,1017 +ddtrace/contrib/trace_utils.py,sha256=P6aAKM8TPa9uIyFY9P_hqpzdxZN3StLSUBWaE-5qw_s,11813 +ddtrace/contrib/urllib3/__init__.py,sha256=OdDmQM7aNwYk23xWkgnhOox6lRAGL92FQiTmnIxbJZQ,1569 +ddtrace/contrib/urllib3/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/urllib3/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/urllib3/patch.py,sha256=ThzuJQ63gqmxez_zwoT8zV9fotvIv81DvJMgdZJ-SaM,4467 +ddtrace/contrib/util.py,sha256=8sn1gyl9jDAprk3sG4oF5WkqMmDxpaw0ogD2KXBUDPk,438 +ddtrace/contrib/vertica/__init__.py,sha256=ltvuezAMIy7Re1LPQxf6OG7H5nXTQyTUWsoJIQcIP1Y,1347 +ddtrace/contrib/vertica/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/vertica/__pycache__/constants.cpython-38.pyc,, +ddtrace/contrib/vertica/__pycache__/patch.cpython-38.pyc,, +ddtrace/contrib/vertica/constants.py,sha256=DNXQ4AR1Luf7KKq9If70nxsKcDxIZF8b__AlIJzgsxw,31 +ddtrace/contrib/vertica/patch.py,sha256=fH_07TMRwau3Smu71QWZB5F8UHiiad8HCzDzSy4vyRw,7826 +ddtrace/contrib/wsgi/__init__.py,sha256=OV9x6jGLh3zV_7LEbkgFraoevCZIUKF5yOH55cw2EB4,830 +ddtrace/contrib/wsgi/__pycache__/__init__.cpython-38.pyc,, +ddtrace/contrib/wsgi/__pycache__/wsgi.cpython-38.pyc,, +ddtrace/contrib/wsgi/wsgi.py,sha256=Ajhld7OzQY1kFiJSsvX8GMnE9HBN7IaubD5IwUtTSNc,5130 +ddtrace/encoding.py,sha256=nQWQw04nBrvJHR6GkvREJrplKocPJk95dP-tzheDJlE,513 +ddtrace/ext/__init__.py,sha256=CW4WZyARim4DC7NBBfwZ18A_bWXf9FYEp-xsLl4z_m8,309 +ddtrace/ext/__pycache__/__init__.cpython-38.pyc,, +ddtrace/ext/__pycache__/aws.cpython-38.pyc,, +ddtrace/ext/__pycache__/cassandra.cpython-38.pyc,, +ddtrace/ext/__pycache__/ci.cpython-38.pyc,, +ddtrace/ext/__pycache__/consul.cpython-38.pyc,, +ddtrace/ext/__pycache__/db.cpython-38.pyc,, +ddtrace/ext/__pycache__/elasticsearch.cpython-38.pyc,, +ddtrace/ext/__pycache__/errors.cpython-38.pyc,, +ddtrace/ext/__pycache__/git.cpython-38.pyc,, +ddtrace/ext/__pycache__/http.cpython-38.pyc,, +ddtrace/ext/__pycache__/kombu.cpython-38.pyc,, +ddtrace/ext/__pycache__/memcached.cpython-38.pyc,, +ddtrace/ext/__pycache__/mongo.cpython-38.pyc,, +ddtrace/ext/__pycache__/net.cpython-38.pyc,, +ddtrace/ext/__pycache__/priority.cpython-38.pyc,, +ddtrace/ext/__pycache__/redis.cpython-38.pyc,, +ddtrace/ext/__pycache__/sql.cpython-38.pyc,, +ddtrace/ext/__pycache__/system.cpython-38.pyc,, +ddtrace/ext/__pycache__/test.cpython-38.pyc,, +ddtrace/ext/aws.py,sha256=Qr0aaE7Y5kky7cpKT25BWvqXoCwhD7xIhUXmOPJZA4M,1400 +ddtrace/ext/cassandra.py,sha256=pImop6ncMOKJa-nAZBCL_9_i9HfMHF-x0rA5W8Cqvpc,225 +ddtrace/ext/ci.py,sha256=6huk4wee-zEA_p9tphpKmWdPx0LGz_p2SLdyOxeggyc,16796 +ddtrace/ext/consul.py,sha256=6Vq9cGCCOimwFMyQRR1rx0mnEWYKWn3JSYVihLnkPhk,76 +ddtrace/ext/db.py,sha256=L-gPiwB0qCAvgv43dsQLLFNu-e0smtKUo1o_PvD0MRw,170 +ddtrace/ext/elasticsearch.py,sha256=1NbDiLzJYpzs0KVC7W2q8MsHkcayjFjZwasFP6mRSkI,211 +ddtrace/ext/errors.py,sha256=B7q8UL2nz2xhvhZcTy3crK0-1fVPcPRT8HVFZQD7tgw,949 +ddtrace/ext/git.py,sha256=CxHi28xffo9qW8RKruyt-o-ebW7db2P72utkC8tgq5w,5796 +ddtrace/ext/http.py,sha256=7IycEiu_9n22D8TyGJl7NUKqUi86ZAgdSfSKeG1S87Y,367 +ddtrace/ext/kombu.py,sha256=JqzezXNOH_wlcrQzNAOeM87v4CnJPFXrNeK9hX-UDTk,228 +ddtrace/ext/memcached.py,sha256=Hw1lKBwbo3IfqLrwtcg25IrCE8fRMWRleB5GkFZYpR8,74 +ddtrace/ext/mongo.py,sha256=88NuhD-kD_ojl5RvErmH0eERk1Mi_HrCvqZXSAz-pM4,118 +ddtrace/ext/net.py,sha256=x0b0s_FqdkQhBC_iAAePfDCzzl_tLmhsOuriff57qiY,129 +ddtrace/ext/priority.py,sha256=7b5cwGjhQ0aTjgcpsJQrh5TvCXXU3IjKtTL0QIhuwdg,874 +ddtrace/ext/redis.py,sha256=KBJ8ktQSXpm7PUzI02uHzaqipmNYNbP9VfHLzol5TSE,262 +ddtrace/ext/sql.py,sha256=KpGYyfPtUTYrJAqWGbXPhwimRqd2vmk5JMpkXV9Enqo,950 +ddtrace/ext/system.py,sha256=2URo46I-dmpIArgaVWUXZYKBMt-tLO_bH9kHG97Ygy0,255 +ddtrace/ext/test.py,sha256=1YNhBQWTWjcPuDMeYvLirfaV0tNOl2Ds7I2LqWX_6Vw,900 +ddtrace/filters.py,sha256=DlHUe15T8X9clO59yyxs-ZoUSgDggOs0mJV63gYROnk,2486 +ddtrace/helpers.py,sha256=ML3YXziquFw28Iy9m-6AC1Ug7uZnf5AHBgw9BQPlLok,1752 +ddtrace/http/__init__.py,sha256=mkfA55FuwXk56anWtYvsnqUTLWxHvpOGK36xC_LKpAI,355 +ddtrace/http/__pycache__/__init__.cpython-38.pyc,, +ddtrace/http/__pycache__/headers.cpython-38.pyc,, +ddtrace/http/headers.py,sha256=8wAxw_sMnzcFp11KtuWnN4EhO8p1ZniEVz7B4iyLh5U,931 +ddtrace/internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ddtrace/internal/__pycache__/__init__.cpython-38.pyc,, +ddtrace/internal/__pycache__/agent.cpython-38.pyc,, +ddtrace/internal/__pycache__/atexit.cpython-38.pyc,, +ddtrace/internal/__pycache__/compat.cpython-38.pyc,, +ddtrace/internal/__pycache__/debug.cpython-38.pyc,, +ddtrace/internal/__pycache__/dogstatsd.cpython-38.pyc,, +ddtrace/internal/__pycache__/encoding.cpython-38.pyc,, +ddtrace/internal/__pycache__/forksafe.cpython-38.pyc,, +ddtrace/internal/__pycache__/hostname.cpython-38.pyc,, +ddtrace/internal/__pycache__/http.cpython-38.pyc,, +ddtrace/internal/__pycache__/logger.cpython-38.pyc,, +ddtrace/internal/__pycache__/nogevent.cpython-38.pyc,, +ddtrace/internal/__pycache__/periodic.cpython-38.pyc,, +ddtrace/internal/__pycache__/rate_limiter.cpython-38.pyc,, +ddtrace/internal/__pycache__/service.cpython-38.pyc,, +ddtrace/internal/__pycache__/sma.cpython-38.pyc,, +ddtrace/internal/__pycache__/uds.cpython-38.pyc,, +ddtrace/internal/__pycache__/uwsgi.cpython-38.pyc,, +ddtrace/internal/__pycache__/writer.cpython-38.pyc,, +ddtrace/internal/_encoding.cpython-38-darwin.so,sha256=8xtTnew6b-3RRSVKXEkofGddkuisDCEc6uCQTk_LCV4,227536 +ddtrace/internal/_rand.cpython-38-darwin.so,sha256=anDu2Ba3WjeyFNdW9InTPchlkh-uwTI85Tfld8oep2w,61128 +ddtrace/internal/agent.py,sha256=7PY3TcNtmeaz7Ou1lRBYRTlZXgp7zrPOdHylnlNpbc4,4403 +ddtrace/internal/atexit.py,sha256=xp4vsleQ4iH1vJMOPTr39bPRGO0uTl6z-KgXsS9MtT4,1798 +ddtrace/internal/compat.py,sha256=LQChcevcBJSRc_ZiYIZLMjRgZISq2j1FRbPdftJlj2w,6882 +ddtrace/internal/debug.py,sha256=WJ7vxQqOzUkf_-dzs0GIdInoCaz9UcnMYFHIsTxBUu0,9617 +ddtrace/internal/dogstatsd.py,sha256=Wf9ofudhIjdeIG_PEJSFdwimVTxig5OVdeGYq4o-Pgg,860 +ddtrace/internal/encoding.py,sha256=KgJkoXB7tFyeHUaaBxh6eFLoxodPNoQBwhQrYpYvwnI,2736 +ddtrace/internal/forksafe.py,sha256=lUy7VMMhihi7m4S7NJlZPp8vpSAKLp3CC3aHvTPoE80,3897 +ddtrace/internal/hostname.py,sha256=YPniL_XNPuI4pf_N5XvRR8HNSFtEvX_gw_QY4YKey8g,233 +ddtrace/internal/http.py,sha256=F8XLr90sCfPG_KuAPwppsOdJoSwYzWjJB97Yaon3egw,1103 +ddtrace/internal/logger.py,sha256=8qYG7WObnP3muOCisG2FKFZa7cBTzoECjRbwX4MmVbU,7508 +ddtrace/internal/nogevent.py,sha256=MkgNZJaHw1iuB0pHMZUAFqnIw-ZQL0qBfNYyNtL7cAw,2601 +ddtrace/internal/periodic.py,sha256=CSRZSVL153r9dOvyIKC5SEFLpWL7-UodFK8xAyrKVCU,8379 +ddtrace/internal/processor/__init__.py,sha256=E76wtmhFV_pmQBYf0SxDLChAuJU9NoiewRU9HeS1W1g,1516 +ddtrace/internal/processor/__pycache__/__init__.cpython-38.pyc,, +ddtrace/internal/processor/__pycache__/trace.cpython-38.pyc,, +ddtrace/internal/processor/trace.py,sha256=N4h1KL3X8BClYJ33_Hlh78uKALQYRwrjdSDGt2qr4jw,5509 +ddtrace/internal/rate_limiter.py,sha256=B2kN11YieL4JDMPsp2GTTGO6VNOVlztPm74z2E3n7SM,4726 +ddtrace/internal/runtime/__init__.py,sha256=0pXSt9-wyQAqHhGTP1rUEQ_zc8dK9Iax7GJqE_oMJdE,476 +ddtrace/internal/runtime/__pycache__/__init__.cpython-38.pyc,, +ddtrace/internal/runtime/__pycache__/collector.cpython-38.pyc,, +ddtrace/internal/runtime/__pycache__/constants.cpython-38.pyc,, +ddtrace/internal/runtime/__pycache__/container.cpython-38.pyc,, +ddtrace/internal/runtime/__pycache__/metric_collectors.cpython-38.pyc,, +ddtrace/internal/runtime/__pycache__/runtime_metrics.cpython-38.pyc,, +ddtrace/internal/runtime/__pycache__/tag_collectors.cpython-38.pyc,, +ddtrace/internal/runtime/collector.py,sha256=hf6NC5sNyzkCZMcomEweSCpE_eZqDqN0NStnfNEfvqk,3065 +ddtrace/internal/runtime/constants.py,sha256=bsVI_bvp4YNOCO8oh8JZIegNf8H5nRzMkpLGo4nPayw,1200 +ddtrace/internal/runtime/container.py,sha256=RIyJ_CbuALSLlyDUk7k3-O3fgr9F7UdTZE61sgd3Iiw,3574 +ddtrace/internal/runtime/metric_collectors.py,sha256=Xyzq9KO2-bCOgwbTXx75vgEs5ODuLv1crlLekAJJqBQ,3349 +ddtrace/internal/runtime/runtime_metrics.py,sha256=8d2EHPDrrIoBVy8PqcnA9x-4EZXeg2YXEP5YTi5vTSU,5310 +ddtrace/internal/runtime/tag_collectors.py,sha256=wE0sDJNyEY6mnRhlI4_S8RuQMBMPAoKl4KMM6l90xAc,1742 +ddtrace/internal/service.py,sha256=UNVBANovWev3zcVGJjhqtoFRJx0x0uygT16egz44b8k,2952 +ddtrace/internal/sma.py,sha256=CZJTxxoBnFeMN0_xgn-cms3y3BQfMCBTX0Z_H4N1WVY,1601 +ddtrace/internal/uds.py,sha256=X8WGVq_jLbct2QPBAId3l_OgysTHYttSYnBJ_pO2TGU,834 +ddtrace/internal/uwsgi.py,sha256=hM1z4afz2FiCTZeBIJPl7MrgPDLTonATbZxak0kfXd4,2643 +ddtrace/internal/writer.py,sha256=SdU32_F3IrACSw3UmTWitHOaj3JxGNuXxa_v44WtH38,17974 +ddtrace/monkey.py,sha256=0VgDVfoRDgpT1X2r0F_BBKLxhVmgM05VKiHei6a2aWU,8412 +ddtrace/opentracer/__init__.py,sha256=Z2NreXCsKituj_xkrBrxox6dJME-HY5dAQdoPrT6P4A,121 +ddtrace/opentracer/__pycache__/__init__.cpython-38.pyc,, +ddtrace/opentracer/__pycache__/helpers.cpython-38.pyc,, +ddtrace/opentracer/__pycache__/settings.cpython-38.pyc,, +ddtrace/opentracer/__pycache__/span.cpython-38.pyc,, +ddtrace/opentracer/__pycache__/span_context.cpython-38.pyc,, +ddtrace/opentracer/__pycache__/tags.cpython-38.pyc,, +ddtrace/opentracer/__pycache__/tracer.cpython-38.pyc,, +ddtrace/opentracer/__pycache__/utils.cpython-38.pyc,, +ddtrace/opentracer/helpers.py,sha256=BoojCCOpCc_clAOHcAmCEOvoynxCJGXGSsuJt1JScEM,458 +ddtrace/opentracer/propagation/__init__.py,sha256=dM61Rvq_oyksFQii8bRAUFjm2CZXksIsSf_5fSCjoQw,71 +ddtrace/opentracer/propagation/__pycache__/__init__.cpython-38.pyc,, +ddtrace/opentracer/propagation/__pycache__/binary.cpython-38.pyc,, +ddtrace/opentracer/propagation/__pycache__/http.cpython-38.pyc,, +ddtrace/opentracer/propagation/__pycache__/propagator.cpython-38.pyc,, +ddtrace/opentracer/propagation/__pycache__/text.cpython-38.pyc,, +ddtrace/opentracer/propagation/binary.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ddtrace/opentracer/propagation/http.py,sha256=1XQ6G6hBylaboxedAQXazYKZ7Flkri5Y75jY_C2_ap8,2895 +ddtrace/opentracer/propagation/propagator.py,sha256=Qd9Ktx3GfatKBxNegGetuMvePLLoSZFZ9Eo3GA1lIA8,252 +ddtrace/opentracer/propagation/text.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ddtrace/opentracer/settings.py,sha256=6hMOEWVXteDJLnY3aeM7J9wjtaBKse31VFIc3XNsecU,966 +ddtrace/opentracer/span.py,sha256=IK3hklRO9wbUNJTXXXHAKEt2PYWrnf9BAs9GoHMTe7E,6242 +ddtrace/opentracer/span_context.py,sha256=WtRDu883T1WCpabUwXhINHsxmbwVIZuTG6TmsbwkMLE,2210 +ddtrace/opentracer/tags.py,sha256=I3E0S02ZOV1jxBcdo1guoNhsAMyaeaGlI7LsnnuoSPU,450 +ddtrace/opentracer/tracer.py,sha256=hP0FewbyLddxtxEuk6rRFP6fOyDSJPclc8-9_v2gpw0,15992 +ddtrace/opentracer/utils.py,sha256=BJyYWhPWNUjzDO88R6jD9zabfAP90Vkdyc49YnmI-iY,2156 +ddtrace/pin.py,sha256=7zPcjFGinIGV3SLEJ2sB-8PP3Lcky5wlGiIjKIczAys,7750 +ddtrace/profiling/__init__.py,sha256=55iYcZRG6WJMVRH5cF_iocmCVlPa3HyIVayeqimh9-Y,583 +ddtrace/profiling/__pycache__/__init__.cpython-38.pyc,, +ddtrace/profiling/__pycache__/_traceback.cpython-38.pyc,, +ddtrace/profiling/__pycache__/auto.cpython-38.pyc,, +ddtrace/profiling/__pycache__/event.cpython-38.pyc,, +ddtrace/profiling/__pycache__/profiler.cpython-38.pyc,, +ddtrace/profiling/__pycache__/recorder.cpython-38.pyc,, +ddtrace/profiling/__pycache__/scheduler.cpython-38.pyc,, +ddtrace/profiling/_build.cpython-38-darwin.so,sha256=M1gh7qwZBD7i4CuAsXjmu6JiNcfAq6NCBJSMCXXQQFA,39968 +ddtrace/profiling/_traceback.py,sha256=gtTV5xW0HKO2azPwCNlx2PdJ0KnM4TcKo7EeJm8cSrU,111 +ddtrace/profiling/auto.py,sha256=-gFCU-SK-b4PSWE8OY8BSWG6P2swCyS2Ca6cfhB9kGA,164 +ddtrace/profiling/bootstrap/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ddtrace/profiling/bootstrap/__pycache__/__init__.cpython-38.pyc,, +ddtrace/profiling/bootstrap/__pycache__/sitecustomize.cpython-38.pyc,, +ddtrace/profiling/bootstrap/sitecustomize.py,sha256=zSmUmKEOjy4hq1Jiw3yIrhGUQRCl7W3LAHLlLbRbvU0,431 +ddtrace/profiling/collector/__init__.py,sha256=6cFnJVq2ydnIDDI3KXot-_tzs78eoy-W3tT9NOOnPvU,2093 +ddtrace/profiling/collector/__pycache__/__init__.cpython-38.pyc,, +ddtrace/profiling/collector/__pycache__/memalloc.cpython-38.pyc,, +ddtrace/profiling/collector/__pycache__/threading.cpython-38.pyc,, +ddtrace/profiling/collector/_memalloc.cpython-38-darwin.so,sha256=I6ClVJ67FiP526O19ASZBrDMYDlt8H_xk1fNGBZGJA8,40432 +ddtrace/profiling/collector/_task.cpython-38-darwin.so,sha256=Po2VjaybBuTLF-AkgbixStkgs4Uqsba99ljK_Qn316w,73664 +ddtrace/profiling/collector/_threading.cpython-38-darwin.so,sha256=a1xG0R6WLOrCvaUcYPcwEQp2Qi54Dl2riT2nQMZCH3c,45056 +ddtrace/profiling/collector/_traceback.cpython-38-darwin.so,sha256=vms1Evg5uMgg6RH_uk6WxD9OODGPmtSNSaD-esZqTiM,43288 +ddtrace/profiling/collector/memalloc.py,sha256=-YQOcLvOiGxOrYMYrXOn85rBciFCtMtT1cPKMsFdTXU,5717 +ddtrace/profiling/collector/stack.cpython-38-darwin.so,sha256=qTNKxolqcd6n77qzaN3fmv5gEMHZGXCQVefYELRa_g4,271240 +ddtrace/profiling/collector/threading.py,sha256=hAkYVcGgaN5iVcwIwLURKBKIwgtUU0EaWwiJrkzt8EI,7222 +ddtrace/profiling/event.py,sha256=Gd61FZkRzdQMQredSkZSFdjgxCyYJwsRabTDfgvN-tY,1756 +ddtrace/profiling/exporter/__init__.py,sha256=i_bSYLlRxMaCQG1UfinGviQFAHqN3ZUd8UrUzSYXs-I,653 +ddtrace/profiling/exporter/__pycache__/__init__.cpython-38.pyc,, +ddtrace/profiling/exporter/__pycache__/file.cpython-38.pyc,, +ddtrace/profiling/exporter/__pycache__/http.cpython-38.pyc,, +ddtrace/profiling/exporter/__pycache__/pprof_pb2.cpython-38.pyc,, +ddtrace/profiling/exporter/__pycache__/pprof_pre312_pb2.cpython-38.pyc,, +ddtrace/profiling/exporter/file.py,sha256=g49Q0Wcoyv968z-opIcOp6RvnM8UjEYSC1_EAYpJBn0,1000 +ddtrace/profiling/exporter/http.py,sha256=Dywj14SZb5LMXK9PgnI3k3bcW0P57Awk1pzh1vLQjlo,7292 +ddtrace/profiling/exporter/pprof.cpython-38-darwin.so,sha256=HzFm7rIEvV-Eb2VoLXjxOvGNMYmMtE5xfXMZxAQDIoI,457080 +ddtrace/profiling/exporter/pprof_pb2.py,sha256=K-waNza388ffg2MtAdxZZRLkhyQTt1SxIZVCmBPTMWk,28618 +ddtrace/profiling/exporter/pprof_pre312_pb2.py,sha256=lj4Su40oadwotRGrDGW8lG774Vs7d-Pu7AsEt_hzSuI,24821 +ddtrace/profiling/profiler.py,sha256=uc-iesyBjLwYOceBfrfe_HSooWaCVqIE8mQssB_F3_U,9632 +ddtrace/profiling/recorder.py,sha256=n_tSEab9QILn0dMSA1OvawVO6dJ5DXBRd_NUwqY_3wI,2930 +ddtrace/profiling/scheduler.py,sha256=YpqcH-2OJ4F9DNdRACLdL8TmxoCl3UyuN-XaQdD4MXI,2395 +ddtrace/propagation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ddtrace/propagation/__pycache__/__init__.cpython-38.pyc,, +ddtrace/propagation/__pycache__/http.cpython-38.pyc,, +ddtrace/propagation/__pycache__/utils.cpython-38.pyc,, +ddtrace/propagation/http.py,sha256=eebpozo9uF5Xt8-gXOt-ubL_MpukHrBfPGJXZ0AVxS4,5875 +ddtrace/propagation/utils.py,sha256=6_wd3_Zd9rHWvhMlV7bgs51Abja0HrP2-yDnMcqsj1Y,954 +ddtrace/provider.py,sha256=llmCp7s4NVvi23eHqiW_UNbGf7b59pLMJdSdiALAcg8,4439 +ddtrace/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +ddtrace/runtime/__init__.py,sha256=AR66LSao3DEPCaWd_N6IG_0ndUIVtOEy7VQPHcIMucQ,1804 +ddtrace/runtime/__pycache__/__init__.cpython-38.pyc,, +ddtrace/sampler.py,sha256=xu8eDZWk-VZPk8ULc71tlXQaNz66IBCcnypQvi7jeoo,16210 +ddtrace/settings/__init__.py,sha256=K7w4Jo7H8WFNSUlCP_cTwhHHG4C-oO8LlC7yBpYb4x4,319 +ddtrace/settings/__pycache__/__init__.cpython-38.pyc,, +ddtrace/settings/__pycache__/config.cpython-38.pyc,, +ddtrace/settings/__pycache__/exceptions.cpython-38.pyc,, +ddtrace/settings/__pycache__/http.cpython-38.pyc,, +ddtrace/settings/__pycache__/integration.cpython-38.pyc,, +ddtrace/settings/config.py,sha256=dYSppcV9dCi02HzuRpclGf6ZmzDt4KGto6uWX_HFyys,9544 +ddtrace/settings/exceptions.py,sha256=w0BejP6qaqnGpwCBsn8oJ65gEmTg1ESDmvDrKS_YRDo,163 +ddtrace/settings/http.py,sha256=u7bR8cVcRVA1MYy4ta9hArAir6bHt_Px4fu6lH89WmI,2850 +ddtrace/settings/integration.py,sha256=4dL7bf883tL4bQqINM36T1wM9AkAf0VIbcXb8IUw374,5203 +ddtrace/span.py,sha256=NS8lSAgFLYEEPNivJy0HhUT0ky0AE7ozl-n4AYc2Z-I,17733 +ddtrace/tracer.py,sha256=G8igS2L8AOivxgdDvvPEWkifyJNZv-Ad3JwYQK9ucmU,40027 +ddtrace/util.py,sha256=qsoEPYeFesf2Oi0R-fhjUFCx-WXEaKppQd-lOUn3z-Y,557 +ddtrace/utils/__init__.py,sha256=lMXYoU9XYL3uB6QIfVt_v-yHV1Uh_55PtVkMRFwvrc8,1455 +ddtrace/utils/__pycache__/__init__.cpython-38.pyc,, +ddtrace/utils/__pycache__/attr.cpython-38.pyc,, +ddtrace/utils/__pycache__/attrdict.cpython-38.pyc,, +ddtrace/utils/__pycache__/cache.cpython-38.pyc,, +ddtrace/utils/__pycache__/config.cpython-38.pyc,, +ddtrace/utils/__pycache__/deprecation.cpython-38.pyc,, +ddtrace/utils/__pycache__/formats.cpython-38.pyc,, +ddtrace/utils/__pycache__/http.cpython-38.pyc,, +ddtrace/utils/__pycache__/importlib.cpython-38.pyc,, +ddtrace/utils/__pycache__/time.cpython-38.pyc,, +ddtrace/utils/__pycache__/version.cpython-38.pyc,, +ddtrace/utils/__pycache__/wrappers.cpython-38.pyc,, +ddtrace/utils/attr.py,sha256=8ch3kfteqHbO1Xvx9d_NuUl_ZXg_NVst6Mi-F4BEnOY,352 +ddtrace/utils/attrdict.py,sha256=0UNAIOd2uiWZoFfkYInPzXy8Syboa1swwnFRXRYZV4Y,1168 +ddtrace/utils/cache.py,sha256=EzwNnOcMMQvv5EbV-tCkCSK24u7xQ2Y-flWfde-2xmM,2348 +ddtrace/utils/config.py,sha256=TH5zocgnIjK7qTDRrstzg1NmUNfSsf1THOIkRbxTo_Q,433 +ddtrace/utils/deprecation.py,sha256=3Cu04XWo-NOXKehHx7pDWDnyNd63GfkcaOJyjOgDqdQ,3619 +ddtrace/utils/formats.py,sha256=hbEKur4TEbBdoQe1eLr-fLzr3sKxqQp9dzwdyjakt84,4583 +ddtrace/utils/http.py,sha256=jbhE6CSwi1B02-WoLL96AbuFJ5SG0aUEYg2u62OOJoM,787 +ddtrace/utils/importlib.py,sha256=CAEockrG7xLz35BJ0Mtlva8YTAk2sDJ7VI9i6MPQ2D0,1343 +ddtrace/utils/time.py,sha256=YfVjqlsM19bdbqPJ8MMzMTz2z0FNSLpD8uNRM6C-GLI,2235 +ddtrace/utils/version.py,sha256=EdME5hpsAlyPiqP7P2gBUjOyXVkzxC3KKk0NrU4hUW0,1570 +ddtrace/utils/wrappers.py,sha256=9yDqDerBKY8xGTL5H1spGdlBUNqBiRyUZJNiFdAsDro,2984 +ddtrace/vendor/__init__.py,sha256=GqCDtlK1rzjZ4vG1bJtvUGYqaH-LKMCqURI9BchMVYw,2519 +ddtrace/vendor/__pycache__/__init__.cpython-38.pyc,, +ddtrace/vendor/contextvars/__init__.py,sha256=TRvgsEausfPDhHwEBJ-z2tLSMWRC6E_088v4ODkcnXg,3898 +ddtrace/vendor/contextvars/__pycache__/__init__.cpython-38.pyc,, +ddtrace/vendor/debtcollector/__init__.py,sha256=Q8OE09M7ZpXatj2Ft47FVL_ClA5KY5upavDWXg1DKtE,2175 +ddtrace/vendor/debtcollector/__pycache__/__init__.cpython-38.pyc,, +ddtrace/vendor/debtcollector/__pycache__/_utils.cpython-38.pyc,, +ddtrace/vendor/debtcollector/__pycache__/moves.cpython-38.pyc,, +ddtrace/vendor/debtcollector/__pycache__/removals.cpython-38.pyc,, +ddtrace/vendor/debtcollector/__pycache__/renames.cpython-38.pyc,, +ddtrace/vendor/debtcollector/__pycache__/updating.cpython-38.pyc,, +ddtrace/vendor/debtcollector/_utils.py,sha256=OXhJruEi9X2H0EaVIcNkByltmf2VKkczf2IAfOB71SE,6346 +ddtrace/vendor/debtcollector/moves.py,sha256=tapV2utvk2OtYcM_joe_l3Yg3XuTtEZkbeVT-tfKcCQ,8421 +ddtrace/vendor/debtcollector/removals.py,sha256=0q5MvpcXKP_W1Jx6Qp4URBwYqYc3q_rsJRgKT3VW-Kw,13890 +ddtrace/vendor/debtcollector/renames.py,sha256=Lok-0KYHlVCCho9vkFsFPFI6VtgUzk0z5NdUyEUH9wA,1715 +ddtrace/vendor/debtcollector/updating.py,sha256=2x54f0_UKhs_SsGHWzt0zpEFq6M0_RzmC7O42ZXaFLo,2426 +ddtrace/vendor/dogstatsd/__init__.py,sha256=AD8b1g1ld26XDE1NgUtknfyv0qrI931nLa0ohpwTda0,277 +ddtrace/vendor/dogstatsd/__pycache__/__init__.cpython-38.pyc,, +ddtrace/vendor/dogstatsd/__pycache__/base.cpython-38.pyc,, +ddtrace/vendor/dogstatsd/__pycache__/compat.cpython-38.pyc,, +ddtrace/vendor/dogstatsd/__pycache__/context.cpython-38.pyc,, +ddtrace/vendor/dogstatsd/__pycache__/context_async.cpython-38.pyc,, +ddtrace/vendor/dogstatsd/__pycache__/format.cpython-38.pyc,, +ddtrace/vendor/dogstatsd/__pycache__/route.cpython-38.pyc,, +ddtrace/vendor/dogstatsd/base.py,sha256=gx8dL6iYwYmkmzSCfV__jcUvhIeyl2Ji3m9Mt1M72js,23200 +ddtrace/vendor/dogstatsd/compat.py,sha256=_WXnttqiPGQ9r2qOWfDXfJvv82nIiUUPQg1huCfjll8,995 +ddtrace/vendor/dogstatsd/context.py,sha256=DcGU8obtgG9uA8jhwKssY0wvse6PZYPEWxfWddoL-7Q,2722 +ddtrace/vendor/dogstatsd/context_async.py,sha256=aLj58WPZjyhGRn2y6IXaC3nV3ustPJJkA7eAIceSehM,1473 +ddtrace/vendor/dogstatsd/format.py,sha256=maACZlLz8DuSv1sNyhQQQtgswyLPiR98HiHU6cwhxRE,1025 +ddtrace/vendor/dogstatsd/route.py,sha256=3jR6_z_Z-cAkEVNAri7n8GSoZzw4wW8cwjy_DDsNZVI,1265 +ddtrace/vendor/monotonic/__init__.py,sha256=1wJOetpAPQUteaP7IxAelyChpkITsxZf-eV4V2bTHrA,7117 +ddtrace/vendor/monotonic/__pycache__/__init__.cpython-38.pyc,, +ddtrace/vendor/psutil/__init__.py,sha256=zLnjfXMB3aOXBkgwVbiaTzFYKvDpY3bRBS12BryUMn8,90581 +ddtrace/vendor/psutil/__pycache__/__init__.cpython-38.pyc,, +ddtrace/vendor/psutil/__pycache__/_common.cpython-38.pyc,, +ddtrace/vendor/psutil/__pycache__/_compat.cpython-38.pyc,, +ddtrace/vendor/psutil/__pycache__/_psaix.cpython-38.pyc,, +ddtrace/vendor/psutil/__pycache__/_psbsd.cpython-38.pyc,, +ddtrace/vendor/psutil/__pycache__/_pslinux.cpython-38.pyc,, +ddtrace/vendor/psutil/__pycache__/_psosx.cpython-38.pyc,, +ddtrace/vendor/psutil/__pycache__/_psposix.cpython-38.pyc,, +ddtrace/vendor/psutil/__pycache__/_pssunos.cpython-38.pyc,, +ddtrace/vendor/psutil/__pycache__/_pswindows.cpython-38.pyc,, +ddtrace/vendor/psutil/__pycache__/setup.cpython-38.pyc,, +ddtrace/vendor/psutil/_common.py,sha256=eA2kZtQPvlmMpKw_Fz3lswXSg35tthoM6OAb2UhdNNQ,20224 +ddtrace/vendor/psutil/_compat.py,sha256=c9jBW_7ZcDyl562gIeIh53drZ-oUVLPobOnuAJRhY2w,11191 +ddtrace/vendor/psutil/_psaix.py,sha256=IDY57Ybv5k4eSKH50lD7EJ9slfRoPJ-SLKyniXQFvkw,18564 +ddtrace/vendor/psutil/_psbsd.py,sha256=kvDbgjD38KtIZDhsTNbmMowvjSmKs701D-aYr_5wPQE,30566 +ddtrace/vendor/psutil/_pslinux.py,sha256=N-p3vkd-QG9132CIihCIZ47mPa_vwBWkHZf6x3pI6Xg,79839 +ddtrace/vendor/psutil/_psosx.py,sha256=JbNktzY5i5xQJTWNWdWbFSRPBZneb4_34Pm6GKyDiZs,17196 +ddtrace/vendor/psutil/_psposix.py,sha256=sQajYsNSDFV0HqN3GFf7Rvh8vu9eQLbzMpD2eqgakVk,6159 +ddtrace/vendor/psutil/_pssunos.py,sha256=ZayYw299DPsmA8TzA7UpuFuigW49OC0KrdrU4A1hOlY,25109 +ddtrace/vendor/psutil/_psutil_osx.cpython-38-darwin.so,sha256=XFYhxHoCXbeT0LLtD1G1DUSCkof8Dju0T1VfshX_0UE,65232 +ddtrace/vendor/psutil/_psutil_posix.cpython-38-darwin.so,sha256=pA0PNC2zOw0BhmyrPexGb1NSjY9SscD64zycfgZONyc,65232 +ddtrace/vendor/psutil/_pswindows.py,sha256=A2mlRzUvPxPrj8LETN25MAn6fRcLp12L-RP7bwP2TKM,37400 +ddtrace/vendor/psutil/setup.py,sha256=Ze5fZ_gj3Y6e4i3Wb446CgVdiICXpfLYkZVvlllusqo,7931 +ddtrace/vendor/wrapt/__init__.py,sha256=FS1Cu1Ai6qjtdTDMKVtgCTb0gW4mPOlM2111JlqpFq4,658 +ddtrace/vendor/wrapt/__pycache__/__init__.cpython-38.pyc,, +ddtrace/vendor/wrapt/__pycache__/decorators.cpython-38.pyc,, +ddtrace/vendor/wrapt/__pycache__/importer.cpython-38.pyc,, +ddtrace/vendor/wrapt/__pycache__/setup.cpython-38.pyc,, +ddtrace/vendor/wrapt/__pycache__/wrappers.cpython-38.pyc,, +ddtrace/vendor/wrapt/_wrappers.cpython-38-darwin.so,sha256=ONxK3mm-v9g3I9n0neBbnBcHWryS4BOwmz-eg7jxeRw,74152 +ddtrace/vendor/wrapt/decorators.py,sha256=mUjuO9o-Gx0QI9ycp-N6txBv9cDwUf_7n2HlgtOmBGs,20247 +ddtrace/vendor/wrapt/importer.py,sha256=H-d3dCnVxGzz6UTrH4Xncqet5yPX9k-Lo5upa9hlQsc,7896 +ddtrace/vendor/wrapt/setup.py,sha256=CF2p_6VhgEGASbK2JH_MARGMt3GHe6uADKRqu573QY0,191 +ddtrace/vendor/wrapt/wrappers.py,sha256=IAcZRdrc38Bd-vq7Cekq65kAvoO1DXSzjISDQ1Hm6I8,33768 +ddtrace/version.py,sha256=baS8Td1e5xpdw4FfJmWDP8dBZ8xj6CIKk1RtyMLJ-eE,458 +ddtrace_gevent_check.py,sha256=hxO0Ljq9o8RBK1zMw5N34P73YXIG3LnjBcNqq6Oeho0,447 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/WHEEL b/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/WHEEL new file mode 100644 index 000000000..505a8e600 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/WHEEL @@ -0,0 +1,5 @@ +Wheel-Version: 1.0 +Generator: bdist_wheel (0.37.0) +Root-Is-Purelib: false +Tag: cp38-cp38-macosx_10_9_x86_64 + diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/entry_points.txt b/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/entry_points.txt new file mode 100644 index 000000000..c80af2699 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/entry_points.txt @@ -0,0 +1,9 @@ +[console_scripts] +ddtrace-run = ddtrace.commands.ddtrace_run:main + +[gevent.plugins.monkey.did_patch_all] +ddtrace_gevent_check = ddtrace_gevent_check:gevent_patch_all + +[pytest11] +ddtrace = ddtrace.contrib.pytest.plugin + diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/top_level.txt b/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/top_level.txt new file mode 100644 index 000000000..9580c4763 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace-0.55.4.dist-info/top_level.txt @@ -0,0 +1,3 @@ +benchmarks +ddtrace +ddtrace_gevent_check diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/__init__.py new file mode 100644 index 000000000..789dd6efc --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/__init__.py @@ -0,0 +1,34 @@ +from .monkey import patch # noqa: E402 +from .monkey import patch_all +from .pin import Pin # noqa: E402 +from .settings import _config as config # noqa: E402 +from .span import Span # noqa: E402 +from .tracer import Tracer # noqa: E402 +from .utils.deprecation import deprecated # noqa: E402 +from .version import get_version + + +__version__ = get_version() + +# a global tracer instance with integration settings +tracer = Tracer() + +__all__ = [ + "patch", + "patch_all", + "Pin", + "Span", + "tracer", + "Tracer", + "config", +] + + +@deprecated("This method will be removed altogether", "1.0.0") +def install_excepthook(): + """Install a hook that intercepts unhandled exception and send metrics about them.""" + + +@deprecated("This method will be removed altogether", "1.0.0") +def uninstall_excepthook(): + """Uninstall the global tracer except hook.""" diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e2218416231ace655f42c7e96b0bf8edeb00f851 GIT binary patch literal 1037 zcma)5y>8nu5SCI$cDvFjS_0rs$GaZ9ucANv;jqFn^IV{u!AMHhE14n(hraAa{u^otnB7KdW17~p~H zu)(c^w^;B7l50YS)uP$G z+f7!*8b;3zVbfv9Q(IvFt1B{bz&#;-wJRwc^(X;vb5XQsKR zhij1;S%qeEXDza>@glMo9U&lBBVR&nTa>A0gM$5rmPh!mDCtbF_eMVTD=>Zc(QE}U PJ$GaN@uSe~`Jwv{&=CIM literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/_hooks.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/_hooks.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ad9ea4ffe66bf04c36e304a11f6897876872110b GIT binary patch literal 3758 zcmcgvPjA#l6rZuZyN)+m0!6g6w76;!YzqlcgvzQyRFJe)rBF3gfTe12JmbZ|YnvHw zLRQPAM1n)9)GtuMk?&LW;A>Ak_g1M=-i`}XXd>(@BQAN{b6CjA#k1l z?N|S&4k3Ty%k0&FmrHQVpJ8Bx(U^GnG-5h5JcA-_#^%uSEJa&DH@t?T8$dU`rlOlb zx4f33TR_{Mt!Nt0d2`HWbFnjQdu>X1o6kR>-U6T7>@bJ5AJeZ0n`aBVge~xW4@{U_ zWOSW$JAZ*jWUWgJn{l26X|@Bj`DKC+`?4R${zlA!ZeQV{KZ^4!QIO-*)hv(F#E)_H zI?w;2zy@e%!1I2b4hCFYgV%&G!7Hicx=M?_OTYTcd6&FMK;GApxV6c&9gR}-zDAxTVNe{c32b6wZCY5ou+r> z_;<&JOVMtSCSf!f2_IE=(~V6Ytz`Q{f6eh-A)1lnh)yVQv8DNBTCfa>G#) z;6~{vmMi!CVHWdVPrss+OIpBE=*K`U;V!w9CMS*yes{!We!Q!8;!?&#H%hJOajnTWRhoG)+rVmVA>#)VuSpIN@Jk!)|CKEHWZocS!t zPWSH(k_#8c=T74_$d<1yXHj-4N@NZuJGC{=F6`_K`7&d<@Pqo<*EY#^3j2%LA8s)J z;`<=`Z@9_0qr9|KPJrT8ltg*a3jH8Y#ZF=6I~iXK3|u|E@Z8ne-+`B$JRtXtoKDE5 zF`*BPiP499orrmu?3$tt4}lztrd9^UNFIltJ>;)j=z1+k;zk9_ zN*h-jES`nJ@gIa!1d-2EITSxM`^Tg6MK*{f5pevkT^-%9cF@{FIAMSM`*Z zfZMc$iopzLu&{7%-C9c%e(RPSh0mEz-cCm`b2oTp!J4^}t9h4vPrAsaUZTw|d+tgE z`U_xPuAFsG2;%WOI!dbnI^p{5Nb*Xx8LlX~SFw6QXn<9wypYDQdJAre8SD{#0>LvO zPobbrNEz}_t4gOsL9$SpXGUS669gs<9p);+Tie3VGA>q-BeEuRo{o>|&@P!keoCP( z$cszU)oWctybWs$(`T$}X{H~*2XO=k%oJJ%W;&Hh#Ct$}47bYGDEG6R;P zp35L6theF5jQc)^VS+i1+-Zt)|Xq`8%ljNX9a^sF8&QgMZH%VgLfX)N*iQ)eN zwySKuTILe~4rKdj37qw3DAFOkE8Ir8yX|Ap=}HONWt70KI(lD`UG|K|y=&tej6-C# zS2C*4`O0*JTk-mzL%H(Ci&>)bmre?Z<2p{NNhtRkj7OB6w|r+sh*ZFYf_sh3 zs_N_d=F~=6M~tbK_K-sjdJrqzq7H+4(Q|beJ6zmw6?d+|fQW=DMj3r-Tp>U9?px!| z#F`ir^A}3Vb#k{e#pkNVXKrp<3@Y}-!tU=e^uae_%?-F&12Eh4zuyzHs&xb4Vy89> zN@YE?k!pHoir-7_EbXi~R6AaohGjmJo^=lP#Q@Uacd3j}m>;|FVKn4PE-}kvb;Kto z`RBE&oTfdh*~HL*))QL5oozh_uG~%qdrkf&7i@7V2`V#r7(yA;6r)6|c}!MW z*gYn(nYa19pdTe+y4Gz>GahvN#oWrhfU92vg$bR1(clVhjT&x+h58pwt$|L9FM!YD z!zfP?JPLLP4gl;r2s=l^4yU`&%)?3NnyI(B8g2KYzQPVCH3L5-`5GHcKnxfPY*g9%8P6OrbixT+R0oYm6zn#P! r&Y#5LI1B{^>*)qS$=ORi=%}?RE@EMnhhVUwd{uvjZOogt>CpcGnz!3W literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/_version.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/_version.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ad441a46683fd9088786eb95c5bdc95e07e18081 GIT binary patch literal 252 zcmWIL<>g`k0<(D&6DI)a#~=^6&$G0wGBE&w^aA}5{esK_-ORk= ylEj=G-Lk3z%gV~!RQ;5clA^?91RW2xS+Ah-7Kcr4eoARhsvXFE#Y{kgg%JP|`9j_R literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/compat.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/compat.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..159ed0941ed3dca083036c57a1e962490fa03feb GIT binary patch literal 1945 zcmdUvTXWk)6vySnj_-DC=YCagg%n~tNm~e%Qn#e7A(Mo}Waz$VWSxzpR$6IR>$LWF z;DH&QdFLzTfft_o6_|nPIjf|U=RQ>${q;LXd(PgD_NQVoXW(`B_h0Qw)-e7evRTb@$a8E?^E~oA zo7cR6yucPTFCs6pMa@gdOKeH=GV(H8)@&l1%+$PsyuwyAuOhFqRn2S2Yiv#PI`TSO zN8W&2@Ckehn{XRGgU?|L>d=5DY{L%h!X3B^_uvco687LL_!{oRH}C+yg@@3BNAMWF zgME0?TV)$O0Z-u>wBh?-Xor6|H8v04P-Sc;uJXV`$sJp{fp~*;LB5&CM~9vLpF3x* zlT)jGYPHTsv?wAvVca(uFnir&(- z_iX>G;)d7rXoPL*^;uT9Q;*A=!LSSs!??*u~e*Xq)i5q;$% z>Pv2s3(e^sR~EJq+@T3sl1E`6BFdS^Ptq39brl7sFO&*Bw~Lr_Bg@64JucCzxsemd zps!rP&-+5>L3h?wIBms!-K%fA!crHK+koFDeETK0>}Vhy3phsGDxg$ZKM;WmgzKPD zcxPCGauoci7uwFtn~4=gys+A)2B{FINFy(Vw!+S_!*~hGp$r@zMV4}X9w#eG?gchj zegJ)sV$zaJ+l?n4>BYQ>&c>#DKA>j*rO2tk>f=xYp!jTGdR&kplUNioMKVT`CdrWC z8DmH~a%7&QKvEz9bR_=AJ=Ty?Obp(W;0$H^TLkIF6RR6OAe+A%$pdh04Z$G#2PVQ zxt?c=!1MxvRe=#EZiX_0b5mi;u;2Au$7~(9>%-J`y;*N=j$S2&?enX&j~iq65H7il zumle`Z?V~%KCq~~sQ%s`Cr5DHtbEh=^}s)#CBI5rmhE|#^@kyE(>{>=-D`x>y|fi? z;9uG}4?L`*s4?7b>~+Pf7tMPwcI5W12%FaHp4i(P-f7a+3mctA=!RRah!p1Bx*Udk vgMrT**Q@vL9q(v899(7Y$J_dV4zB1ol)^|M@xga;B=N!bpUI8u9RIupo-5QE literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/constants.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/constants.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0e4c0692889458c43432127ff5ea2afcfefd8b9e GIT binary patch literal 1137 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 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/encoding.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/encoding.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1920856ad2bcedee036bbee74c4a9d996fe9500e GIT binary patch literal 638 zcmZ9JL2uJA6vyKvZMv;RRN@2pn!}QG1Efk5f&&L8Z6G#w$R%=PFY|C5%XTxG@4$gC zu=~DJuAKM^NJu=Fb`)&opWk13@5S%=(`2&m!FqB3jUeCizB{wK24G&pV?KgVk0RmK zC`ZV#5A5gungie<57rz4hxuU512L?lJOUo(BNow!j_4lU|LP(SF1&dB13}>!_0d~d zHk4_=gVT%m=YQ>F$6g)5_vx9b6e&M%Lawr7mqeM;tR$8G<@ zsnw)p33Gqtvg+vN4Z~Z9>xM1`!#Bj>1!EF?%P6)D-sW*>nU+LgA}PM(LSWfo(a4Hv z$iRwdi<@g~p{QCe1TXQM5ATw0FiWx|i-SItq-K3oGh;{v&@ye{nIc}pxyTcXqJM!>jD+Rf7WLf+kG zFK>_Z>{zO-_)^LFynCLxRjKqcRa_l%X)M${Y&$hyt!kETM)COG4lhxwesuC@g0I}| Rnr9$mwAn#8^3i(#`VF6DvCsej literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/filters.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/filters.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d776c6940ff39fac7b48ffd84c09a7eae9dc3d6f GIT binary patch literal 3077 zcma)8>uwyk6`tE(w7S&kCCC+EkplLn&a9HUK`a=C3&%}@1WI7bC_1$<<_@{LBhO7F zSC)hoP{4kHyg=dn>!=5K!G75b~+;moci+c7fIa7ZqB_?>gUa}MusZVntgpZ@22 z{^OeC{6vHEPk_NKy!zj0gd^O{iJGq)xfpwym-~_L(wvWe5Cyg$px=o)w%^IRc`xdr z-xa;ApRYx0t|L3@0rvE>LB1ZXyUrJmSQCS1ju^<^Q$O0kcwKB@ydgI*-W2XVXSn$@ zP&m6ow+_BZwZXUlZDrE3;2HXxd;k3IPJH{zJGa07YWE9FZ0#wY$nR8{NUcltvy08N zcVrbzE;&jn!kj;teu{wNUTm?LAVwv08Xx)$Xuc_v5(Wxs2m_5XX5b<{9-j;`m_B zv-TICkCS?fk2T3Qxiqsg=nggqyOLRUtqVEQ-oq5UagzgqCV`!Fc7jEPW zPx#NAC=h|@;M);hIKNl_uF>GGJeW&u^tZ)*mGLV$qnT&r94;eGb>pwJNXndCM&yHo zn{8~&HQZbl*1hfQWzFU)YpJm}OEpU}t~E3KF);`KYC6w}6P#rT16EETa5~SpB0?yT z9U;LgZa~aJTWTmt^VmZWh)OsC5=&y6JW8^;01Qhk0*yr>`&&!5Cux?QUuNZ{6P6my zR9PCP5k@R23&X(>V0!e3bPRFAqelQeWxNm!8w{yJ^ub(;h9&TFl$*_BdZ1;NZD12xeZG9f2vfu04njPwiNGhfU& zOgSs(nILO7u|(XMn-yWHeAD3okV+?v2MCMWrQo<6k)$tsd(M6aY`=m=VH|_0aDIR|pURg!_ zKW4JY?oC89Qc2`u zT1@$!aym)mQw*-!?&>yi$g{a*l+RbvDAPcGyQz>zW|>u5WaPS#eKmOjS|d|4y0u(g zNuz_G%t|CcTlxr-NOgj2ec_5S>_%%yumBjRBI-@0iBT3*5B{kA`**)l?@_PQWaN$| zh1#I0fJ#m6qq3;CFn)m`_71Rvc}{;h*Vefl%_o@3gnk&>zN)wtO T_1!+KfjY=oqE66p7S+b)h_o$(q6Ct1 z;=yE<{y=`9XqWw?!rQL;3tjb)l9DM93ZyQ1$$QT^e7rh38U!ehetXaUii6;9*STFH zbe^D^B^n-ZB7>YHgg85d9VVf-JFq)R$J>#Nk_i5;?B;P2lR$*}Ir_2e<^80Oe$0Ek z|1RWr`5iv^mAoU#9Uff6p_qhyH2>x0v&H%IXXig&%znT?cV3lJ z%wP?;D78qL6&$^Iu30Md^~rpb8JY{bDL7?{)4br76!di_CA|_JU4>v1 zviAt^v#>f7J7Fj?(epxUA(^J#n)sD#@KcEtJW)(1QCwg>o|}{Mx3K9V4>9(o8`d zW`GR>+JdqxW1*@2AG8LOUM_v8EXQW#z+96u1tv4U!U&3#A1F3R#Ty?IP(|jncd>mS zH;gVagVKvqC`Ub0YuY%V5ToBS%Xra_jR$_^?a7sKa=Ki`y8Uj_xh%l5W3s|dRK~{z z=jHM;ElTkdQ*13-N>5*2JivmYT5s?JpjIajjv7yy7D^n;ObK19R~baWgtHox+pd)^ za>}{KPq7GM(|Y&J$4TD1$oK z$o2mWw0lAR-AqcI?Qjz(JEkYALgKKQ$@R(P^h&+nP9JT*)+gVna=Li4R;Q=e52voI z%gKCFX613FjK!%v-d&fc`+Y7Z4Y&{gMz(ReKYZ~CRgT;x{TxkjKO%7_CQ;ZY5k5V{ z*oe$V1Mu9J5J$U9tvjx9kDKnHN$xlK#uW~Y8Mn_0ZmI4%|7Weg)=J~4418!${#J7r Zjzj<3`o($d-V+aNNbmvDuoI#7$$z%kol5`! literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/monkey.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/monkey.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a7dc09671ae7e7f49ead59e744b21fc7cd5fb018 GIT binary patch literal 6240 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 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/pin.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/pin.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b3e12d40449c5ac76e87c22c9d8dbaf26cda2307 GIT binary patch literal 6035 zcmbVQTXWmS6~+P}36YfKi+oGm1i95>BU6r(w6!L7lh{t;X-a!iwMp0u1YwsHC=j3* zpk#9Bht{6SQyx0gx6X9r>62eO)3;842wwY?pOEyW-`NEz%9rUh;9$8}?Dd@Qobw(0 zYH_h@;ClD3fAT+i$uRy+5A&Cehxc)(e@4L#&LX4F+Kf@%MBQwgs&1ifwJlY*QMcQ+ zs+XctyM(^wsNAo#D`=~5Cvy4=?FCg|h^qZsyT%M*%LwnPQN6#|Uc~c)SbD(P%e>Y; z#_QtP8KZstV}md9rH2Mz5+@#5?UTIpOQW&;4R&TUSZ;j~kML*T2!jm&&h0@KCb1u( zzPS12otxhEPi|iS`K{K+Eo$LnI}4I15CEc!&=u_I4{(z<`Z%RK9 zvR(JMpZPrLdV?@-m%UU(9WS8e=dVifkp2@DVYC@H+9qdhi<{iyHZSoquXM|8Ta0H|_gTa! zQgv~qB6Bl{FpRryUqFJ8qMO7RI`Cx{2E)jgssn}uNgP8b&|FiE-Q2-sdcMvua>I^0 zN`~&fA7^U*H8=G~YkIJo?I$h|I~^fKoT<^Y(zc%p>~5H5LZ;2CYNuUYyLL^j=3aHv zyHS{lX0i6_h4rjISjP!ulW$*W92xPNdwWkv8FE3}hg3R=BpD1u5O%^~ZvRJyQO~O` zq}WV~;j6S7wlCyf7>KJEXt|3|Ek~W6x52RQcg5ijbITtLay#?8XyVs4cTp7P}iLUNoeRF*f4lagqngP!j_r`-OqfuY17rUJO zLzT_JD0e(1NzYrwnklVs)T*qke^%A3vT>dCJ?$2dkC|RnH*qTglLv;#;9Sp+xlIVoOG-+QS-#+T%DH*ooInsPjgRS!WfTTmVGbKF zYkkk=drIXcbiGc4NT!_ZgcMKzu#}$Fu}>=iw8n2@rqx2cSKmn2T-d4lC+pS~W#Fhb z9@|Pga|Wa=;sq@6gcb?2QVgW$eM*~9s+^%zzX) zHerw0eRE<;dt#af!^ZF0oDqKge*oSyv-)cuWrWiRqhOCye>kCO~J&E1F@z&wi0mTaQFrSli#OB(+zhMCN*6ic9#lYBF1UI|J=hQ@k|06#ogRZ%)fqi|_dxIuu-52+mInxskN!YBfx46%G-h zj!gOuE4N7sxrH*fAkHT-RZ@bz4RoZ6N-jC9%52=@g_+oo7{U})$`F2!aoR&XFy)B} zaP?3+k@J~ zc*K}7F)8%;yFmIJ3<(q9DoGH+p>TIU%ytyky5XLP3(Yj$TOC⁣Yx&<_9~9aLiP& z1%Yh2a4Kr7iW-0uQI$B+ZOMKtah+X>1F{gryF-e3&ij2nUr( zg|l5pUd|BCC&UlS-2B=;FzGEb;qim6|a3bVE6sQ<%!gyd& zU)#v4yH&{Kx20kl-UvY0;ETsq}5oIZ$f19r&O#_K`u{Tq@qd1Iu)0wpuCT=JjKT4 zd3v(&F(xnJTg|Ww*s8=tIXGm5F_&-CC`$Qsp%N7W^Z{<|+{z|gTHRc+s%#0rv7-zI z5)2giML)WZDEPN%7{5ab&HzDTC7A^VV&6Bj(m`3dIcs8Z1N`GMDR+WC%=p}hUxi8? zsPxx`N*8)n%6qsqr&1`sVYkLV)FPCk55rJFR7LZrBq^_8GmYBw6eM#x}a(}z8Ce6=urVmTCY zr#S(T_XKG>^vXzt2NAw~z!#L}{MgYi1&xaObdcLbne?XyB}vM1bsj|mKre;3!`7qT zFfZ$@`bh|qJN*~#bOnV`ud=FBHBER!hf!(%M>At}rbfA?F66v)HQ45~c80Pwis7D@ zS3R$v@L@!C7~|cc9~EyZp2w2_%}7$yD9KV}c%+!Nqf`asZ!c=4agRPJbmcj`1woJr z(x7alMMTN0qy!HiX<&U}0PnDqaM_@Cq6>LV@yw$6U1}!(sl&(nRHH-Zi$$g!h$xz7 z%gR1qp)SfkNzUYs(@#)1%&r|XtCsSC+HAh9pN7p=>Lmy8tDeMt4!1q)@$J%ir&K-R z)aOQhr(#)S1)se}QBFtoPAy{ zx+r%=aFCSVNJ6+aFXgz%h2>>!xH_U9`Xg1NL+8$WT7B0jfpp5h0sJ}q3;+NC literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/provider.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/provider.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b8b71f6b6e73b73310dc99f87a32300dbe904a5 GIT binary patch literal 5081 zcmcgwTW=gm74F;2w8vw|i9;^CS!lw-?u5in_JT-QP_oVnRw53{Wm&b5dfHtz<92$w zXRB*`8O`#5gZ7CB{s6Kc^R^OyfSIzsp=k&?J)`<(N@)*s;;g&mpb1$ z$IllQng*T^|MbW3*XIr6Z#0;G>KNQd%m0dw8O%%!pMI^x>|4HN5^aOFeOu8EXvcRH zT?1Y7Yl=3Lx?g8?)<_zC*LN{jXKpgrZ~9F|&n5HymfuozGg;^_`iqL5PtNt1{3X-i zi~Rh)>0jWjt!36?CR_M}>j&TB#@3}nbKmwaV}6O9$NYJI1>>t2 zUtr4^FZ0X%>b~Q@!OW*d`{Fl{%2;okh5aDi!;iZbCP}!FaH1a%WSpg80=o7*1^JDM z)fm--%`AJ7gKDg0spPv7W_ctGm*WabGjMa;KpC+;eBj= zPfguW=XE;zB<$Viax-H$F%5yh)KH{ap5r_nDW%gny-_In$*poOUu&O;ZdvsZZK189 z<%{T~abO-AVupOkFz?rXL3i>M?pn^1?khvUosECQtFXW}xLL)$Trw)8$mP+)p z|4a*G0#I}G_b0p;=KujXcXyb=U9*(DneXywD7m+@$y2<_fqx1z$e*Djw$fBiL-?|W zR~y_L=A3yUP{m_B%78zam|Api5Q?y`j=1MN142w6MyT>{PNG9jJ+w!qXr}qwiLnhh z8339}UPNz5P_Z&$wz9b^O1YIuE0a6K)d`yytsqPFC57N0kS*wX8FNeK$eU%2>E-{G zP5$yct+<=EN;L=3{@(`5Wj0XQAE&+7FjT2z#4BDgt<)^ugx*f7_i}KwYFc&ovGz#Q zKb)!Q*I~&^&Bs`|=Ty$gOz&Hdp;qw3fk%|>wxE;nSZSW|{ZG_Q_olvk_QPv-X zGTw-jSnjQOpQ`w{8}l*@(502x}LfMrn-h-K_t*x^Q0EQT97#S5uJsvt1cb|ikqThx7SgAbY>*u7J@}^I# zEJid)OAQ4WKlu(m^%6>w|EyXo}%Scw82>%*AX-q zp&fh3wDja3p__J&uZ*s#iVN3wDhE+>R93hs;8a23H@-rG>*E$$(733570ygf0lny+TTN7&?hG(KT}o4XOy+lHXMi(WEG9`89OXkVpdetpn@OFb!!RILx41f8?9BqK3V> z5R`Cf99#?@J`C0#uRr_b^Jkwucv9G?0#quCM12krOH~js^T>#Vo;fv@BUp>(=sUAD zJUfdf+@rQeTTnRA^}?6t*ktC5Ihf2EJ$zCYHOS52RDzsH@UoyQeYLU%+?D*UDt&ZT z2jp6zy5kP^FKiULGvSBOewWx#lGv^}x^b2yuaS{YOhEyIghDP3c_OQby-iMJUmmZni5OAhhxNT>Xb2+=P8hVpXQG2MK6AfR!Z4C+g?hu zgtXJQjBG`sDSLS!Dtk#38`9nl;vt2XgBdg~0!YVO)|GtrE$`iY#NSSWoW z;*_LJ9j?@_I3Rl@!~y;13NTl1)!cfkuI$u)Q&9a^)aBj)kRV9Cg7hos5{eqVv-5bj zFm&bCDD@>M(E8`pe>3nOpB0_CiMivm`rkQ%&J{PQQ!2X-s<83SZ?0(!`z#dNHx_qd zxfvk)36-N4jRzmEA!Q6z&QjE~jV&HYh0*$!scUuAk+_BXkxsaNM%N~P8=Y&m^q=Kg WC1tj37g>>ISpTfouKZ-_>c0Rr_ZO`I literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/sampler.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/sampler.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9e03e3267eca62d549bc5974075519afab854929 GIT binary patch literal 12897 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 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/span.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/span.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cda3b851e5b511290df6ccd81fac73b21f820f62 GIT binary patch literal 12967 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 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/tracer.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/tracer.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..54dc0ddbba2eb5b425b3f8aa11200e27273af845 GIT binary patch literal 26914 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 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/util.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/util.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0341dbd9deb422b4a82efa3417acb3bed64b3373 GIT binary patch literal 642 zcmZ9I&2G~`5Xb#($B9c@ULX=&a)|8!suT&KJ^`1~%U;}VW@23YvD$T9;&nI8hS{D!B`O-3sM{SOmN9K|~)scV!W@%I=GfmAF zvBj*B?b5Cs=8WtRyUZQgCH9y%vRC?5z=Dx|;t89W23ion1VWg8vneuz2u|PyoWfb| zu@KI$jK#~JCN*D}LkK7&Zg`78Zu-X+O-b7Z0q8T8HEXS6&tt9UgW>|NGq2gQ2 z1aCK=b;_>3mt6ESfL8Ji#=EvC`&ZWr=Qqbk>SiwZ<_phpUeu~Z4!ygI(>Pt&gH!Vg z2fsq4C_eZdN<|lvG)zM}T3ju9j?&i>kbGQB*_m#p;-g0#a%_sT=n&jCvf{1!uMRso zMD^VTwfk3h@Ux7UWtROgWTcyEdBQ}-)R(;_i)Hy{$!> zea-9jx__7Im5bz4A_}q8FQ%Hyt{3b5zQW{rGJpU2GO^eX!TYL#U5Ov`)75K|nR&Er O%N+MF&wnQEpMC>R#il&~ literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/version.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/__pycache__/version.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..cde48bef2082bcc3780a4f5d296f26764b26b242 GIT binary patch literal 529 zcmYjO%T60X5bT~cwq;|4to+0t$eM&8S&Aa0JfvKFae@IwTCF{Uvt}Qo-tmH0=Ee{3 z0|<`%Nb}5Ih_)i)}6vB;LRo zNHGN8o3Qc%x7TKVWENvCRac@E38Z)p2um2j5Ncq=MT>qQD+tn)ka*Wvb(eTwGg}Wh z!x7ZsbhWv}S)F%}|LvZ#Dr-YH;hu>TPQnSc2G1NBe6)>HQ4#0aGml_BsY<_Lo8ej| z7tXf7raC9{nJ6dQ(HGN@RFThVq6)h^RVI{iVJIHY4IA;{FpE+ZhW6E`4SkTCFZn!^ zzF3)3gPk9N+|B>|3^c`MWK>W_6rAUY0olkE_1QxKZ4H+UEN-m-9q@a4x@@@E24(e~SPB literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/_hooks.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/_hooks.py new file mode 100644 index 000000000..3283fe4df --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/_hooks.py @@ -0,0 +1,133 @@ +import collections +from copy import deepcopy +from typing import Any +from typing import Callable +from typing import DefaultDict +from typing import Optional +from typing import Set + +import attr + +from .internal.logger import get_logger + + +log = get_logger(__name__) + + +@attr.s(slots=True) +class Hooks(object): + """ + Hooks configuration object is used for registering and calling hook functions + + Example:: + + @config.falcon.hooks.on('request') + def on_request(span, request, response): + pass + """ + + _hooks = attr.ib(init=False, factory=lambda: collections.defaultdict(set), type=DefaultDict[str, Set]) + + def __deepcopy__(self, memodict=None): + hooks = Hooks() + hooks._hooks = deepcopy(self._hooks, memodict) + return hooks + + def register( + self, + hook, # type: Any + func=None, # type: Optional[Callable] + ): + # type: (...) -> Optional[Callable[..., Any]] + """ + Function used to register a hook for the provided name. + + Example:: + + def on_request(span, request, response): + pass + + config.falcon.hooks.register('request', on_request) + + + If no function is provided then a decorator is returned:: + + @config.falcon.hooks.register('request') + def on_request(span, request, response): + pass + + :param hook: The name of the hook to register the function for + :type hook: object + :param func: The function to register, or ``None`` if a decorator should be returned + :type func: function, None + :returns: Either a function decorator if ``func is None``, otherwise ``None`` + :rtype: function, None + """ + # If they didn't provide a function, then return a decorator + if not func: + + def wrapper(func): + self.register(hook, func) + return func + + return wrapper + self._hooks[hook].add(func) + return None + + # Provide shorthand `on` method for `register` + # >>> @config.falcon.hooks.on('request') + # def on_request(span, request, response): + # pass + on = register + + def deregister( + self, + hook, # type: Any + func, # type: Callable + ): + # type: (...) -> None + """ + Function to deregister a function from a hook it was registered under + + Example:: + + @config.falcon.hooks.on('request') + def on_request(span, request, response): + pass + + config.falcon.hooks.deregister('request', on_request) + + :param hook: The name of the hook to register the function for + :type hook: object + :param func: Function hook to register + :type func: function + """ + if hook in self._hooks: + try: + self._hooks[hook].remove(func) + except KeyError: + pass + + def emit( + self, + hook, # type: Any + *args, # type: Any + **kwargs # type: Any + ): + # type: (...) -> None + """ + Function used to call registered hook functions. + + :param hook: The hook to call functions for + :type hook: str + :param args: Positional arguments to pass to the hook functions + :type args: list + :param kwargs: Keyword arguments to pass to the hook functions + :type kwargs: dict + """ + # Call registered hooks + for func in self._hooks.get(hook, ()): + try: + func(*args, **kwargs) + except Exception: + log.error("Failed to run hook %s function %s", hook, func, exc_info=True) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/_version.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/_version.py new file mode 100644 index 000000000..a8850ee3b --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/_version.py @@ -0,0 +1,5 @@ +# coding: utf-8 +# file generated by setuptools_scm +# don't change, don't track in version control +version = '0.55.4' +version_tuple = (0, 55, 4) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/bootstrap/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/bootstrap/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/bootstrap/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/bootstrap/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..df5ae64655208342d263d2cedc2cd8d2a648801d GIT binary patch literal 206 zcmWIL<>g`k0<(D&6G8N25P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;x_UV%Y;MS7m4WtE8m2&5P2hv*k%7U*W? z6_+IDt{a`k; literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/bootstrap/__pycache__/sitecustomize.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/bootstrap/__pycache__/sitecustomize.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b00b34825f65be6d56e0599bc60b6c77cc03780c GIT binary patch literal 2977 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 S2FX#jTER5TvTc~va{U*QtSc)3 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/commands/ddtrace_run.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/commands/ddtrace_run.py new file mode 100644 index 000000000..14ac733da --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/commands/ddtrace_run.py @@ -0,0 +1,134 @@ +#!/usr/bin/env python +import argparse +from distutils import spawn +import logging +import os +import sys + +import ddtrace +from ddtrace.internal.compat import PY2 +from ddtrace.utils.formats import asbool +from ddtrace.utils.formats import get_env + + +if PY2: + # Python 2 does not have PermissionError but Python 3 does. + PermissionError = None # noqa: A001 + + +# Do not use `ddtrace.internal.logger.get_logger` here +# DEV: It isn't really necessary to use `DDLogger` here so we want to +# defer importing `ddtrace` until we actually need it. +# As well, no actual rate limiting would apply here since we only +# have a few logged lines +log = logging.getLogger(__name__) + +USAGE = """ +Execute the given Python command after configuring it to emit Datadog traces +and profiles. + + +Examples +ddtrace-run python app.py +ddtrace-run gunicorn myproject.wsgi +""" + + +def _add_bootstrap_to_pythonpath(bootstrap_dir): + """ + Add our bootstrap directory to the head of $PYTHONPATH to ensure + it is loaded before program code + """ + python_path = os.environ.get("PYTHONPATH", "") + + if python_path: + new_path = "%s%s%s" % (bootstrap_dir, os.path.pathsep, os.environ["PYTHONPATH"]) + os.environ["PYTHONPATH"] = new_path + else: + os.environ["PYTHONPATH"] = bootstrap_dir + + +def main(): + parser = argparse.ArgumentParser( + description=USAGE, + prog="ddtrace-run", + usage="ddtrace-run ", + formatter_class=argparse.RawTextHelpFormatter, + ) + parser.add_argument("command", nargs=argparse.REMAINDER, type=str, help="Command string to execute.") + parser.add_argument("-d", "--debug", help="enable debug mode (disabled by default)", action="store_true") + parser.add_argument( + "-i", + "--info", + help=( + "print library info useful for debugging. Only reflects configurations made via environment " + "variables, not those made in code." + ), + action="store_true", + ) + parser.add_argument("-p", "--profiling", help="enable profiling (disabled by default)", action="store_true") + parser.add_argument("-v", "--version", action="version", version="%(prog)s " + ddtrace.__version__) + parser.add_argument("-nc", "--colorless", help="print output of command without color", action="store_true") + args = parser.parse_args() + + if args.profiling: + os.environ["DD_PROFILING_ENABLED"] = "true" + + debug_mode = args.debug or asbool(get_env("trace", "debug", default=False)) + + if debug_mode: + logging.basicConfig(level=logging.DEBUG) + os.environ["DD_TRACE_DEBUG"] = "true" + + if args.info: + # Inline imports for performance. + from ddtrace.internal.debug import pretty_collect + + print(pretty_collect(ddtrace.tracer, color=not args.colorless)) + sys.exit(0) + + root_dir = os.path.dirname(ddtrace.__file__) + log.debug("ddtrace root: %s", root_dir) + + bootstrap_dir = os.path.join(root_dir, "bootstrap") + log.debug("ddtrace bootstrap: %s", bootstrap_dir) + + _add_bootstrap_to_pythonpath(bootstrap_dir) + log.debug("PYTHONPATH: %s", os.environ["PYTHONPATH"]) + log.debug("sys.path: %s", sys.path) + + if not args.command: + parser.print_help() + sys.exit(1) + + # Find the executable path + executable = spawn.find_executable(args.command[0]) + if not executable: + print("ddtrace-run: failed to find executable '%s'.\n" % args.command[0]) + parser.print_usage() + sys.exit(1) + + log.debug("program executable: %s", executable) + + if os.path.basename(executable) == "uwsgi": + print( + ( + "ddtrace-run has known compatibility issues with uWSGI where the " + "tracer is not started properly in uWSGI workers which can cause " + "broken behavior. It is recommended you remove ddtrace-run and " + "update your uWSGI configuration following " + "https://ddtrace.readthedocs.io/en/stable/advanced_usage.html#uwsgi." + ) + ) + + try: + os.execl(executable, executable, *args.command[1:]) + except PermissionError: + print("ddtrace-run: permission error while launching '%s'" % executable) + print("Did you mean `ddtrace-run python %s`?" % executable) + sys.exit(1) + except Exception: + print("ddtrace-run: error launching '%s'" % executable) + raise + + sys.exit(0) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/compat.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/compat.py new file mode 100644 index 000000000..e2f4c222e --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/compat.py @@ -0,0 +1,80 @@ +from ddtrace.utils.deprecation import deprecation + +from .internal.compat import CONTEXTVARS_IS_AVAILABLE +from .internal.compat import NumericType +from .internal.compat import PY2 +from .internal.compat import PY3 +from .internal.compat import PYTHON_INTERPRETER +from .internal.compat import PYTHON_VERSION +from .internal.compat import PYTHON_VERSION_INFO +from .internal.compat import Queue +from .internal.compat import StringIO +from .internal.compat import binary_type +from .internal.compat import contextvars +from .internal.compat import ensure_text +from .internal.compat import get_connection_response +from .internal.compat import getrandbits +from .internal.compat import httplib +from .internal.compat import is_integer +from .internal.compat import iscoroutinefunction +from .internal.compat import iteritems +from .internal.compat import main_thread +from .internal.compat import make_async_decorator +from .internal.compat import monotonic +from .internal.compat import monotonic_ns +from .internal.compat import msgpack_type +from .internal.compat import numeric_types +from .internal.compat import parse +from .internal.compat import pattern_type +from .internal.compat import process_time_ns +from .internal.compat import reload_module +from .internal.compat import reraise +from .internal.compat import string_type +from .internal.compat import stringify +from .internal.compat import time_ns +from .internal.compat import to_unicode +from .internal.compat import urlencode + + +__all__ = [ + "CONTEXTVARS_IS_AVAILABLE", + "NumericType", + "PY2", + "PY3", + "PYTHON_INTERPRETER", + "PYTHON_VERSION", + "PYTHON_VERSION_INFO", + "Queue", + "StringIO", + "binary_type", + "contextvars", + "ensure_text", + "get_connection_response", + "getrandbits", + "httplib", + "is_integer", + "iscoroutinefunction", + "iteritems", + "main_thread", + "make_async_decorator", + "monotonic", + "monotonic_ns", + "msgpack_type", + "numeric_types", + "parse", + "pattern_type", + "process_time_ns", + "reload_module", + "reraise", + "string_type", + "stringify", + "time_ns", + "to_unicode", + "urlencode", +] + +deprecation( + name="ddtrace.compat", + message="The compat module has been moved to ddtrace.internal and will no longer be part of the public API.", + version="1.0.0", +) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/constants.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/constants.py new file mode 100644 index 000000000..0cc0f485b --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/constants.py @@ -0,0 +1,38 @@ +FILTERS_KEY = "FILTERS" +SAMPLE_RATE_METRIC_KEY = "_sample_rate" +SAMPLING_PRIORITY_KEY = "_sampling_priority_v1" +ANALYTICS_SAMPLE_RATE_KEY = "_dd1.sr.eausr" +SAMPLING_AGENT_DECISION = "_dd.agent_psr" +SAMPLING_RULE_DECISION = "_dd.rule_psr" +SAMPLING_LIMIT_DECISION = "_dd.limit_psr" +ORIGIN_KEY = "_dd.origin" +HOSTNAME_KEY = "_dd.hostname" +ENV_KEY = "env" +VERSION_KEY = "version" +SERVICE_KEY = "service.name" +SERVICE_VERSION_KEY = "service.version" +SPAN_KIND = "span.kind" +SPAN_MEASURED_KEY = "_dd.measured" +KEEP_SPANS_RATE_KEY = "_dd.tracer_kr" + +NUMERIC_TAGS = (ANALYTICS_SAMPLE_RATE_KEY,) + +MANUAL_DROP_KEY = "manual.drop" +MANUAL_KEEP_KEY = "manual.keep" + +LOG_SPAN_KEY = "__datadog_log_span" + +ERROR_MSG = "error.msg" # a string representing the error message +ERROR_TYPE = "error.type" # a string representing the type of the error +ERROR_STACK = "error.stack" # a human readable version of the stack. + +PID = "system.pid" + +# Use this to explicitly inform the backend that a trace should be rejected and not stored. +USER_REJECT = -1 +# Used by the builtin sampler to inform the backend that a trace should be rejected and not stored. +AUTO_REJECT = 0 +# Used by the builtin sampler to inform the backend that a trace should be kept and stored. +AUTO_KEEP = 1 +# Use this to explicitly inform the backend that a trace should be kept and stored. +USER_KEEP = 2 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/context.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/context.py new file mode 100644 index 000000000..b3e53d763 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/context.py @@ -0,0 +1,135 @@ +import threading +from typing import Any +from typing import Optional +from typing import TYPE_CHECKING +from typing import Text + +from .constants import ORIGIN_KEY +from .constants import SAMPLING_PRIORITY_KEY +from .internal.compat import NumericType +from .internal.logger import get_logger +from .utils.deprecation import deprecated + + +if TYPE_CHECKING: + from .span import Span + from .span import _MetaDictType + from .span import _MetricDictType + +log = get_logger(__name__) + + +class Context(object): + """Represents the state required to propagate a trace across execution + boundaries. + """ + + __slots__ = [ + "trace_id", + "span_id", + "_lock", + "_meta", + "_metrics", + ] + + def __init__( + self, + trace_id=None, # type: Optional[int] + span_id=None, # type: Optional[int] + dd_origin=None, # type: Optional[str] + sampling_priority=None, # type: Optional[float] + meta=None, # type: Optional[_MetaDictType] + metrics=None, # type: Optional[_MetricDictType] + ): + self._meta = meta if meta is not None else {} # type: _MetaDictType + self._metrics = metrics if metrics is not None else {} # type: _MetricDictType + + self.trace_id = trace_id # type: Optional[int] + self.span_id = span_id # type: Optional[int] + + if dd_origin is not None: + self._meta[ORIGIN_KEY] = dd_origin + if sampling_priority is not None: + self._metrics[SAMPLING_PRIORITY_KEY] = sampling_priority + + self._lock = threading.RLock() + + def _with_span(self, span): + # type: (Span) -> Context + """Return a shallow copy of the context with the given span.""" + ctx = self.__class__(trace_id=span.trace_id, span_id=span.span_id) + ctx._lock = self._lock + ctx._meta = self._meta + ctx._metrics = self._metrics + return ctx + + def _update_tags(self, span): + # type: (Span) -> None + with self._lock: + span.meta.update(self._meta) + span.metrics.update(self._metrics) + + @property + def sampling_priority(self): + # type: () -> Optional[NumericType] + """Return the context sampling priority for the trace.""" + return self._metrics.get(SAMPLING_PRIORITY_KEY) + + @sampling_priority.setter + def sampling_priority(self, value): + # type: (Optional[NumericType]) -> None + with self._lock: + if value is None: + if SAMPLING_PRIORITY_KEY in self._metrics: + del self._metrics[SAMPLING_PRIORITY_KEY] + return + self._metrics[SAMPLING_PRIORITY_KEY] = value + + @property + def dd_origin(self): + # type: () -> Optional[Text] + """Get the origin of the trace.""" + return self._meta.get(ORIGIN_KEY) + + @dd_origin.setter + def dd_origin(self, value): + # type: (Optional[Text]) -> None + """Set the origin of the trace.""" + with self._lock: + if value is None: + if ORIGIN_KEY in self._meta: + del self._meta[ORIGIN_KEY] + return + self._meta[ORIGIN_KEY] = value + + @deprecated("Cloning contexts will no longer be required in 0.50", version="0.50") + def clone(self): + # type: () -> Context + """ + Partially clones the current context. + It copies everything EXCEPT the registered and finished spans. + """ + return self + + def __eq__(self, other): + # type: (Any) -> bool + if isinstance(other, Context): + with self._lock: + return ( + self.trace_id == other.trace_id + and self.span_id == other.span_id + and self._meta == other._meta + and self._metrics == other._metrics + ) + return False + + def __repr__(self): + # type: () -> str + return "Context(trace_id=%s, span_id=%s, _meta=%s, _metrics=%s)" % ( + self.trace_id, + self.span_id, + self._meta, + self._metrics, + ) + + __str__ = __repr__ diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/__init__.py new file mode 100644 index 000000000..bcd6ddf72 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/__init__.py @@ -0,0 +1,3 @@ +from ..utils.importlib import func_name # noqa +from ..utils.importlib import module_name # noqa +from ..utils.importlib import require_modules # noqa diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..66fad07ae1bd40f9202c7d69afc58fdd0b254602 GIT binary patch literal 325 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 Qi?3m_is6(UyY=0.11.0 + from aiobotocore._endpoint_helpers import ClientResponseContentProxy + +from ...constants import ANALYTICS_SAMPLE_RATE_KEY +from ...constants import SPAN_MEASURED_KEY +from ...ext import SpanTypes +from ...ext import aws +from ...ext import http +from ...internal.compat import PYTHON_VERSION_INFO +from ...pin import Pin +from ...utils import ArgumentError +from ...utils import get_argument_value +from ...utils.formats import deep_getattr +from ...utils.wrappers import unwrap + + +ARGS_NAME = ("action", "params", "path", "verb") +TRACED_ARGS = {"params", "path", "verb"} + + +def patch(): + if getattr(aiobotocore.client, "_datadog_patch", False): + return + setattr(aiobotocore.client, "_datadog_patch", True) + + wrapt.wrap_function_wrapper("aiobotocore.client", "AioBaseClient._make_api_call", _wrapped_api_call) + Pin(service=config.service or "aws", app="aws").onto(aiobotocore.client.AioBaseClient) + + +def unpatch(): + if getattr(aiobotocore.client, "_datadog_patch", False): + setattr(aiobotocore.client, "_datadog_patch", False) + unwrap(aiobotocore.client.AioBaseClient, "_make_api_call") + + +class WrappedClientResponseContentProxy(wrapt.ObjectProxy): + def __init__(self, body, pin, parent_span): + super(WrappedClientResponseContentProxy, self).__init__(body) + self._self_pin = pin + self._self_parent_span = parent_span + + async def read(self, *args, **kwargs): + # async read that must be child of the parent span operation + operation_name = "{}.read".format(self._self_parent_span.name) + + with self._self_pin.tracer.start_span(operation_name, child_of=self._self_parent_span) as span: + # inherit parent attributes + span.resource = self._self_parent_span.resource + span.span_type = self._self_parent_span.span_type + span.meta = dict(self._self_parent_span.meta) + span.metrics = dict(self._self_parent_span.metrics) + + result = await self.__wrapped__.read(*args, **kwargs) + span.set_tag("Length", len(result)) + + return result + + # wrapt doesn't proxy `async with` context managers + if PYTHON_VERSION_INFO >= (3, 5, 0): + + async def __aenter__(self): + # call the wrapped method but return the object proxy + await self.__wrapped__.__aenter__() + return self + + async def __aexit__(self, *args, **kwargs): + response = await self.__wrapped__.__aexit__(*args, **kwargs) + return response + + +async def _wrapped_api_call(original_func, instance, args, kwargs): + pin = Pin.get_from(instance) + if not pin or not pin.enabled(): + result = await original_func(*args, **kwargs) + return result + + endpoint_name = deep_getattr(instance, "_endpoint._endpoint_prefix") + + service = pin.service if pin.service != "aws" else "{}.{}".format(pin.service, endpoint_name) + with pin.tracer.trace("{}.command".format(endpoint_name), service=service, span_type=SpanTypes.HTTP) as span: + span.set_tag(SPAN_MEASURED_KEY) + + try: + operation = get_argument_value(args, kwargs, 0, "operation_name") + span.resource = "{}.{}".format(endpoint_name, operation.lower()) + except ArgumentError: + operation = None + span.resource = endpoint_name + + aws.add_span_arg_tags(span, endpoint_name, args, ARGS_NAME, TRACED_ARGS) + + region_name = deep_getattr(instance, "meta.region_name") + + meta = { + "aws.agent": "aiobotocore", + "aws.operation": operation, + "aws.region": region_name, + } + span.set_tags(meta) + + result = await original_func(*args, **kwargs) + + body = result.get("Body") + if isinstance(body, ClientResponseContentProxy): + result["Body"] = WrappedClientResponseContentProxy(body, pin, span) + + response_meta = result["ResponseMetadata"] + response_headers = response_meta["HTTPHeaders"] + + span.set_tag(http.STATUS_CODE, response_meta["HTTPStatusCode"]) + if 500 <= response_meta["HTTPStatusCode"] < 600: + span.error = 1 + + span.set_tag("retry_attempts", response_meta["RetryAttempts"]) + + request_id = response_meta.get("RequestId") + if request_id: + span.set_tag("aws.requestid", request_id) + + request_id2 = response_headers.get("x-amz-id-2") + if request_id2: + span.set_tag("aws.requestid2", request_id2) + + # set analytics sample rate + span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, config.aiobotocore.get_analytics_sample_rate()) + + return result diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/__init__.py new file mode 100644 index 000000000..930391e3a --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/__init__.py @@ -0,0 +1,65 @@ +""" +The ``aiohttp`` integration traces all requests defined in the application handlers. +Auto instrumentation is available using the ``trace_app`` function:: + + from aiohttp import web + from ddtrace import tracer, patch + from ddtrace.contrib.aiohttp import trace_app + + # patch third-party modules like aiohttp_jinja2 + patch(aiohttp=True) + + # create your application + app = web.Application() + app.router.add_get('/', home_handler) + + # trace your application handlers + trace_app(app, tracer, service='async-api') + web.run_app(app, port=8000) + +Integration settings are attached to your application under the ``datadog_trace`` +namespace. You can read or update them as follows:: + + # disables distributed tracing for all received requests + app['datadog_trace']['distributed_tracing_enabled'] = False + +Available settings are: + +* ``tracer`` (default: ``ddtrace.tracer``): set the default tracer instance that is used to + trace `aiohttp` internals. By default the `ddtrace` tracer is used. +* ``service`` (default: ``aiohttp-web``): set the service name used by the tracer. Usually + this configuration must be updated with a meaningful name. +* ``distributed_tracing_enabled`` (default: ``True``): enable distributed tracing during + the middleware execution, so that a new span is created with the given ``trace_id`` and + ``parent_id`` injected via request headers. + +Third-party modules that are currently supported by the ``patch()`` method are: + +* ``aiohttp_jinja2`` + +When a request span is created, a new ``Context`` for this logical execution is attached +to the ``request`` object, so that it can be used in the application code:: + + async def home_handler(request): + ctx = request['datadog_context'] + # do something with the tracing Context + +:ref:`All HTTP tags ` are supported for this integration. + +""" +from ...utils.importlib import require_modules + + +required_modules = ["aiohttp"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .middlewares import trace_app + from .patch import patch + from .patch import unpatch + + __all__ = [ + "patch", + "unpatch", + "trace_app", + ] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0027a6fd0c28ce10ae825a7d0d2e17f6582884c8 GIT binary patch literal 2382 zcmaJ@%Wm676r~=vY{r4R>Uw}IBo#I#r)dxiaZtBF(QJw~b&yttL5?^hXuFiml0?mctvxo7nD@nhe^_56?D`CmJp_cwjH zeeL4Q=lClk+=MswL_;)_M%o-V8=h#*8snC1%GR%q-(HN{vR%9pZLyVXrQ73ejJL&h z(n&kxj(hIld1t(X=N+*t{8?+f`_B8!`_6mg{osj5-+TQ%{<`CN{@GlzDB>!cTboA_ zQ`*Xz;Z|juS;J#lFrFmL$X`lX*n$Z;Ray$nV-`z!o+m15`hLD;3>6`rmnl-mWyl_I#^NUYo5v?hvWkG zV^*@5YsiiZmKj#&m@H`>)KIW#mL%C_u@cD>CR9PPf;<&OrzRi``5|9Y*%SdP4aZU~ zFt8HyishfWcc667@nRFl;X|BIYRVzH=cw3MJSn8_pRNUcn*wO}6Ev0Emw~8zXc}H7 zc7&I;UW3(Oe+2%mW2Bx{*TwY@*D>vyTZZl_3r8wYyTxqXwXSK62Bd&}v7{EIY!%CO zzGxk_iW4}5b>;6ttVMi?@<-7veD!Qbb*jKjmM%%Pmw>%3O7OfSfOFtQvrW~ktj#1X z3(F>0=t?QrrLuFzSt_{(xzjRnaVuQ^!~Q+2)MyT5Rh$pjUz7$J94g6DC16sQBvg4N z($}=BM@9&vAsg?)$cc5ORkO`bkEll;RqRuN-U8BIIR(8 zQYyWu_w4&4Bd4S26erx*XJ_9s%VAF6<@?MMuwu;h&zN-yiPw&vK9(wSeOe z7TgTrwxry__0iyXqAxFo&o7>uqfd1{46kPT`1tx6PK4obmJiMbxylde;782E#dUtX zT&8kRd(iqn)e9y)sF@jrq0-8RVURCxc3xJ*y&}4bk8x`s?X|X>exu#kqfe{Zz~|rp E0M51+rT_o{ literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/__pycache__/middlewares.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/__pycache__/middlewares.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b65813a3fcd82f39d0a1862141c1b0ecdb8a76da GIT binary patch literal 3603 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 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/__pycache__/template.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/__pycache__/template.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5be62a5c48ceb76bd21f11c973ae447389bb780b GIT binary patch literal 979 zcmYjQ&2AGh5cd9Wev1$)aLWN9<sE{O)onQ$Rj`1%4%*IirTh`6Br_$>TSioQ|V8ryFq?n1`jNx(%s z20DW=7D0cQLXUYc-3~fO>@?ZhO?JfgBgVI)vjOXLrjNI@40zBDc+59qmP?$b8{;YO zOePr(2r1!Q>)wz_5g!Ftj0LOKHu*%YM`diZoMW^)sz6)$I&8?P8g-*;H;txIw_urf zz!&%7r0om+nBmu0)+;9j=+C4miF8IXPV;geWqgVC#&!>qmH%u>>2v)`a?PJ_*3CN9 F`Uh|-3~>Me literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/middlewares.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/middlewares.py new file mode 100644 index 000000000..7cb42f4e8 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/middlewares.py @@ -0,0 +1,149 @@ +from ddtrace import config + +from .. import trace_utils +from ...constants import ANALYTICS_SAMPLE_RATE_KEY +from ...constants import SPAN_MEASURED_KEY +from ...ext import SpanTypes +from ...ext import http +from ...internal.compat import stringify +from ..asyncio import context_provider + + +CONFIG_KEY = "datadog_trace" +REQUEST_CONTEXT_KEY = "datadog_context" +REQUEST_CONFIG_KEY = "__datadog_trace_config" +REQUEST_SPAN_KEY = "__datadog_request_span" + + +async def trace_middleware(app, handler): + """ + ``aiohttp`` middleware that traces the handler execution. + Because handlers are run in different tasks for each request, we attach the Context + instance both to the Task and to the Request objects. In this way: + + * the Task is used by the internal automatic instrumentation + * the ``Context`` attached to the request can be freely used in the application code + """ + + async def attach_context(request): + # application configs + tracer = app[CONFIG_KEY]["tracer"] + service = app[CONFIG_KEY]["service"] + distributed_tracing = app[CONFIG_KEY]["distributed_tracing_enabled"] + # Create a new context based on the propagated information. + trace_utils.activate_distributed_headers( + tracer, + int_config=config.aiohttp, + request_headers=request.headers, + override=distributed_tracing, + ) + + # trace the handler + request_span = tracer.trace( + "aiohttp.request", + service=service, + span_type=SpanTypes.WEB, + ) + request_span.set_tag(SPAN_MEASURED_KEY) + + # Configure trace search sample rate + # DEV: aiohttp is special case maintains separate configuration from config api + analytics_enabled = app[CONFIG_KEY]["analytics_enabled"] + if (config.analytics_enabled and analytics_enabled is not False) or analytics_enabled is True: + request_span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, app[CONFIG_KEY].get("analytics_sample_rate", True)) + + # attach the context and the root span to the request; the Context + # may be freely used by the application code + request[REQUEST_CONTEXT_KEY] = request_span.context + request[REQUEST_SPAN_KEY] = request_span + request[REQUEST_CONFIG_KEY] = app[CONFIG_KEY] + try: + response = await handler(request) + return response + except Exception: + request_span.set_traceback() + raise + + return attach_context + + +async def on_prepare(request, response): + """ + The on_prepare signal is used to close the request span that is created during + the trace middleware execution. + """ + # safe-guard: discard if we don't have a request span + request_span = request.get(REQUEST_SPAN_KEY, None) + if not request_span: + return + + # default resource name + resource = stringify(response.status) + + if request.match_info.route.resource: + # collect the resource name based on http resource type + res_info = request.match_info.route.resource.get_info() + + if res_info.get("path"): + resource = res_info.get("path") + elif res_info.get("formatter"): + resource = res_info.get("formatter") + elif res_info.get("prefix"): + resource = res_info.get("prefix") + + # prefix the resource name by the http method + resource = "{} {}".format(request.method, resource) + + request_span.resource = resource + + # DEV: aiohttp is special case maintains separate configuration from config api + trace_query_string = request[REQUEST_CONFIG_KEY].get("trace_query_string") + if trace_query_string is None: + trace_query_string = config.http.trace_query_string + if trace_query_string: + request_span.set_tag(http.QUERY_STRING, request.query_string) + + trace_utils.set_http_meta( + request_span, + config.aiohttp, + method=request.method, + url=str(request.url), # DEV: request.url is a yarl's URL object + status_code=response.status, + request_headers=request.headers, + response_headers=response.headers, + ) + + request_span.finish() + + +def trace_app(app, tracer, service="aiohttp-web"): + """ + Tracing function that patches the ``aiohttp`` application so that it will be + traced using the given ``tracer``. + + :param app: aiohttp application to trace + :param tracer: tracer instance to use + :param service: service name of tracer + """ + + # safe-guard: don't trace an application twice + if getattr(app, "__datadog_trace", False): + return + setattr(app, "__datadog_trace", True) + + # configure datadog settings + app[CONFIG_KEY] = { + "tracer": tracer, + "service": config._get_service(default=service), + "distributed_tracing_enabled": None, + "analytics_enabled": None, + "analytics_sample_rate": 1.0, + } + + # the tracer must work with asynchronous Context propagation + tracer.configure(context_provider=context_provider) + + # add the async tracer middleware as a first middleware + # and be sure that the on_prepare signal is the last one + app.middlewares.insert(0, trace_middleware) + app.on_response_prepare.append(on_prepare) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/patch.py new file mode 100644 index 000000000..e7c12c7e1 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/patch.py @@ -0,0 +1,54 @@ +from ddtrace import config +from ddtrace.internal.logger import get_logger +from ddtrace.vendor import wrapt + +from ...pin import Pin +from ...utils.wrappers import unwrap + + +log = get_logger(__name__) + +try: + # instrument external packages only if they're available + import aiohttp_jinja2 + + from .template import _trace_render_template + + template_module = True +except ImportError: + template_module = False +except Exception: + log.warning("aiohttp_jinja2 could not be imported and will not be instrumented.", exc_info=True) + template_module = False + +config._add( + "aiohttp", + dict( + distributed_tracing=True, + ), +) + + +def patch(): + """ + Patch aiohttp third party modules: + * aiohttp_jinja2 + """ + if template_module: + if getattr(aiohttp_jinja2, "__datadog_patch", False): + return + setattr(aiohttp_jinja2, "__datadog_patch", True) + + _w = wrapt.wrap_function_wrapper + _w("aiohttp_jinja2", "render_template", _trace_render_template) + Pin(app="aiohttp", service=config.service).onto(aiohttp_jinja2) + + +def unpatch(): + """ + Remove tracing from patched modules. + """ + if template_module: + if getattr(aiohttp_jinja2, "__datadog_patch", False): + setattr(aiohttp_jinja2, "__datadog_patch", False) + unwrap(aiohttp_jinja2, "render_template") diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/template.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/template.py new file mode 100644 index 000000000..fdbdc2816 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiohttp/template.py @@ -0,0 +1,30 @@ +import aiohttp_jinja2 + +from ddtrace import Pin + +from ...ext import SpanTypes +from ...utils import get_argument_value + + +def _trace_render_template(func, module, args, kwargs): + """ + Trace the template rendering + """ + # get the module pin + pin = Pin.get_from(aiohttp_jinja2) + if not pin or not pin.enabled(): + return func(*args, **kwargs) + + # original signature: + # render_template(template_name, request, context, *, app_key=APP_KEY, encoding='utf-8') + template_name = get_argument_value(args, kwargs, 0, "template_name") + request = get_argument_value(args, kwargs, 1, "request") + env = aiohttp_jinja2.get_env(request.app) + + # the prefix is available only on PackageLoader + template_prefix = getattr(env.loader, "package_path", "") + template_meta = "{}/{}".format(template_prefix, template_name) + + with pin.tracer.trace("aiohttp.template", span_type=SpanTypes.TEMPLATE) as span: + span.set_meta("aiohttp.template", template_meta) + return func(*args, **kwargs) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiopg/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiopg/__init__.py new file mode 100644 index 000000000..f7458296b --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiopg/__init__.py @@ -0,0 +1,27 @@ +""" +Instrument aiopg to report a span for each executed Postgres queries:: + + from ddtrace import Pin, patch + import aiopg + + # If not patched yet, you can patch aiopg specifically + patch(aiopg=True) + + # This will report a span with the default settings + async with aiopg.connect(DSN) as db: + with (await db.cursor()) as cursor: + await cursor.execute("SELECT * FROM users WHERE id = 1") + + # Use a pin to specify metadata related to this connection + Pin.override(db, service='postgres-users') +""" +from ...utils.importlib import require_modules + + +required_modules = ["aiopg"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + + __all__ = ["patch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiopg/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiopg/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0db0c6c3d880d0250e42586290673dea9e032a1d GIT binary patch literal 947 zcmZ8f%}(1u5VrF}Xau6%dl=CJNd+Ia@!-@`TmXlu6f?i()rgOkn@^8vrh;2 zLXZ2rmiTEctoa_VkNvQYHLQQ}zdi~Z*f2Z1!JA1lZH7&XFHn5p+UREij?7F<1o}Go{G~lQ9V+r2$zy0ldOEx5(jG89UZ!;4()o(CqHE z+dwwbDh1B1W-)?D9m%nf8;~&@PaI@^>q=EgkKu3xQrQYb>;>8lC{zwH6?d8QmzfM> zF%mILlERU>V8?Oo4Rns(JI2987?_D9`A^DB*a_GP0>=@{6AK2d6>@AG9W#ZDDUC3W zvllBVF}9uk)1xjh1AMsafTdOk>#!LUmhO9Tu8q>2uESN%Uu=o5kX58N?__7~^x*x$ zn*lt5w0=*Ugkn$lq#MlXu_uJ z$*o_7o38h|LEcffb<>I>uHq;PS8^*7)2qguh@qw(^TLO7GJemtk_uy}^LH1HsidLd zM$r#XwKHFN;q{XxZV!>Lpi(H+PQeDx1Y&uE4muXe*gOUR%zob z7zCNfHU;fBac)kpvwl&e7|i$c{!gv4S_}gxFNh)`g^i+KR@^MUthB!>HJc?m8mmk7 Lg<9Kh_)E1v>^L1Q literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiopg/__pycache__/connection.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiopg/__pycache__/connection.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3b0a26615be43928827044d68e3929363991df8f GIT binary patch literal 3877 zcmZ`6%W~YtaqwL1i!V`r_V z!II(@coM68ic|Rk7nz*+5BY|C0IoUt7jj9u2iWD3qOdiMo}R%>zq|K&yWJr0-23V8 z{x4?<`43K}9~&lL!%+VMzzH`ZLh)@z#=xYefw&b}1Do0!w)pUaeiZcWsN;v zUDv|b`mw5s z`U@a}P=ga{a)VkwkZpsT+&c8AEu6l~ZSEWr&__+rTwc>NPtQDF*R#5wHF#6c8ob5Z zhlDnNAU2^b(9ldVyYleKrt|~B?+>L)8(+3&iAqlT%FvJLdYV=RorjIqK7YbJ%|r)kk<+|PWT?6BA$h^}4O zN<`aWUd?-9rD0syxO4*MVl0S!r5HOQ|J`3oWw`5SVrkcxOWR4r!C{t0cb1m7;=TUu zd;L%4ozLR*HhaAjFE5Yo-o|GqUD{kq!}L}dtIUt0Tf3uld4GQ(mN?fcn!LfiG8t|y z`C*doEWs=mK?eTVOZO#2A-M`ec>stpXEfk9dheWNdSh_|RPOk}SZ=~dV5ols$jA{W z`q(&db5~x;-6Qjus84buw{z!5V-=$JkK~}1*EUG*_N@aCpxt+J(sy$&H?vybXWh#ZHBxvxB4gV!89;c%{#GQod>`o4)mD*(Q@$5~CQ<9u+Iaei!aW(w z{2l2c9i5LlPoJ+odr7@&MHM!zE9u|{v_s)&i_&X&A!L(iKMMoJls`x#!6f(-ZQ!>9 z0p<^=H(|6PFX87dEPR)6c>^zNKuVH6asc=E5}^D29=_#ey%(e(MwynRFqFIo#1dS` z>U@bRJJ0Yfp#2Vp(m`$BG)7n7!QNZ>wFaLB z_-O|~PL3v~K-M5sohsO^+}a@WH?ZjpTV4POE0symwMzxtI4Sjek!pKz!0%yHoZh$~ zLyb=i4A?h*5JxY%4?|&aq85*gV~FmODYH})WMevCy8W zeM+L8B&t9n8xU({=?Fv=!2zdUhUCBH;~6;q=B z(JF3{wpNJFThk zx%nfsMxZR~4fM2S#JIE-L7+Rp!VIl-g+qsq7$L3g@j?O6s14J+`4^sC!N7 zqdKrGF{9F^?qqt9`68u_LTgnQt2-S!U(I?DLeTBTS=9z%+|${qMQn7d=p0kAu|an# zo2urRjM0WFrFjW-j!6&~ b-fzny@D)a-!fwk0uSU;4_p>}};fwzRIf!&r literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiopg/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiopg/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..13aa75d1fe15334c3ccee8da3a7a75f3f573773b GIT binary patch literal 1927 zcmZ`(&2HO95a#k?HjI+e=Fz5tXC}Y9KIL1U1@=62R_-C_vEMwIYThiQScL z8S+WKwm@GXXpem*UVG{*^wQ2OsaQ^$6*xOHn%SM1Z)Sh$cH0`Pr*D3XEl1P-LScQG zAbbm-cmsrLlw?|1?j#c%gl5yy^ECg+3u-1CgD9w zjx>MkZ-`MF`J~$U?#CC$JWd#WUgSAT&kl2AhRrNR5uIMSr9WOHc{O&Y(OlB5>3r2PlV zj&zp@t4BGc#`h?2~!S$uihYLG3Qd8*a9=LxGPZR5pB3M2oL7Z>wtBWgyCMp0-f zczmZaaSr(&+|BPIxrYRS<{u%!7I`=7`yUMME3mGofV7RC*(a{fJUyakC=>m9IRXc|}O~*VQ+`PDAPml0RuI&>QSjj z1>2nOqRZaOWhN(?7&MQQ;5J(9pha(GamI35a8+qF>uP$cR#NZ+oO7nSvd+JKlLh}A nPx|0&y3eKson`C*6A+&Padj8!SP!V8hTCx+le8_j-~abNSZ(A; literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiopg/connection.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiopg/connection.py new file mode 100644 index 000000000..4319e53f3 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiopg/connection.py @@ -0,0 +1,111 @@ +import asyncio + +from aiopg import __version__ +from aiopg.utils import _ContextManager + +from ddtrace import config +from ddtrace.constants import ANALYTICS_SAMPLE_RATE_KEY +from ddtrace.constants import SPAN_MEASURED_KEY +from ddtrace.contrib import dbapi +from ddtrace.ext import SpanTypes +from ddtrace.ext import sql +from ddtrace.pin import Pin +from ddtrace.utils.version import parse_version +from ddtrace.vendor import wrapt + + +AIOPG_VERSION = parse_version(__version__) + + +class AIOTracedCursor(wrapt.ObjectProxy): + """TracedCursor wraps a psql cursor and traces its queries.""" + + def __init__(self, cursor, pin): + super(AIOTracedCursor, self).__init__(cursor) + pin.onto(self) + name = pin.app or "sql" + self._datadog_name = "%s.query" % name + + @asyncio.coroutine + def _trace_method(self, method, resource, extra_tags, *args, **kwargs): + pin = Pin.get_from(self) + if not pin or not pin.enabled(): + result = yield from method(*args, **kwargs) + return result + service = pin.service + + with pin.tracer.trace(self._datadog_name, service=service, resource=resource, span_type=SpanTypes.SQL) as s: + s.set_tag(SPAN_MEASURED_KEY) + s.set_tag(sql.QUERY, resource) + s.set_tags(pin.tags) + s.set_tags(extra_tags) + + # set analytics sample rate + s.set_tag(ANALYTICS_SAMPLE_RATE_KEY, config.aiopg.get_analytics_sample_rate()) + + try: + result = yield from method(*args, **kwargs) + return result + finally: + s.set_metric("db.rowcount", self.rowcount) + + @asyncio.coroutine + def executemany(self, query, *args, **kwargs): + # FIXME[matt] properly handle kwargs here. arg names can be different + # with different libs. + result = yield from self._trace_method( + self.__wrapped__.executemany, query, {"sql.executemany": "true"}, query, *args, **kwargs + ) + return result + + @asyncio.coroutine + def execute(self, query, *args, **kwargs): + result = yield from self._trace_method(self.__wrapped__.execute, query, {}, query, *args, **kwargs) + return result + + @asyncio.coroutine + def callproc(self, proc, args): + result = yield from self._trace_method(self.__wrapped__.callproc, proc, {}, proc, args) + return result + + def __aiter__(self): + return self.__wrapped__.__aiter__() + + +class AIOTracedConnection(wrapt.ObjectProxy): + """TracedConnection wraps a Connection with tracing code.""" + + def __init__(self, conn, pin=None, cursor_cls=AIOTracedCursor): + super(AIOTracedConnection, self).__init__(conn) + name = dbapi._get_vendor(conn) + db_pin = pin or Pin(service=name, app=name) + db_pin.onto(self) + # wrapt requires prefix of `_self` for attributes that are only in the + # proxy (since some of our source objects will use `__slots__`) + self._self_cursor_cls = cursor_cls + + # unfortunately we also need to patch this method as otherwise "self" + # ends up being the aiopg connection object + if AIOPG_VERSION >= (0, 16, 0): + + def cursor(self, *args, **kwargs): + # Only one cursor per connection is allowed, as per DB API spec + self.close_cursor() + self._last_usage = self._loop.time() + + coro = self._cursor(*args, **kwargs) + return _ContextManager(coro) + + else: + + def cursor(self, *args, **kwargs): + coro = self._cursor(*args, **kwargs) + return _ContextManager(coro) + + @asyncio.coroutine + def _cursor(self, *args, **kwargs): + cursor = yield from self.__wrapped__._cursor(*args, **kwargs) + pin = Pin.get_from(self) + if not pin: + return cursor + return self._self_cursor_cls(cursor, pin) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiopg/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiopg/patch.py new file mode 100644 index 000000000..8a2fc3d37 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aiopg/patch.py @@ -0,0 +1,57 @@ +# 3p +import asyncio + +import aiopg.connection +import psycopg2.extensions + +from ddtrace.contrib.aiopg.connection import AIOTracedConnection +from ddtrace.contrib.psycopg.patch import _patch_extensions +from ddtrace.contrib.psycopg.patch import _unpatch_extensions +from ddtrace.contrib.psycopg.patch import patch_conn as psycopg_patch_conn +from ddtrace.utils.wrappers import unwrap as _u +from ddtrace.vendor import wrapt + + +def patch(): + """Patch monkey patches psycopg's connection function + so that the connection's functions are traced. + """ + if getattr(aiopg, "_datadog_patch", False): + return + setattr(aiopg, "_datadog_patch", True) + + wrapt.wrap_function_wrapper(aiopg.connection, "_connect", patched_connect) + _patch_extensions(_aiopg_extensions) # do this early just in case + + +def unpatch(): + if getattr(aiopg, "_datadog_patch", False): + setattr(aiopg, "_datadog_patch", False) + _u(aiopg.connection, "_connect") + _unpatch_extensions(_aiopg_extensions) + + +@asyncio.coroutine +def patched_connect(connect_func, _, args, kwargs): + conn = yield from connect_func(*args, **kwargs) + return psycopg_patch_conn(conn, traced_conn_cls=AIOTracedConnection) + + +def _extensions_register_type(func, _, args, kwargs): + def _unroll_args(obj, scope=None): + return obj, scope + + obj, scope = _unroll_args(*args, **kwargs) + + # register_type performs a c-level check of the object + # type so we must be sure to pass in the actual db connection + if scope and isinstance(scope, wrapt.ObjectProxy): + scope = scope.__wrapped__._conn + + return func(obj, scope) if scope else func(obj) + + +# extension hooks +_aiopg_extensions = [ + (psycopg2.extensions.register_type, psycopg2.extensions, "register_type", _extensions_register_type), +] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/algoliasearch/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/algoliasearch/__init__.py new file mode 100644 index 000000000..41bb48da8 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/algoliasearch/__init__.py @@ -0,0 +1,34 @@ +""" +The Algoliasearch__ integration will add tracing to your Algolia searches. + +:: + + from ddtrace import patch_all + patch_all() + + from algoliasearch import algoliasearch + client = alogliasearch.Client(, ) + index = client.init_index() + index.search("your query", args={"attributesToRetrieve": "attribute1,attribute1"}) + +Configuration +~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.algoliasearch['collect_query_text'] + + Whether to pass the text of your query onto Datadog. Since this may contain sensitive data it's off by default + + Default: ``False`` + +.. __: https://www.algolia.com +""" + +from ...utils.importlib import require_modules + + +with require_modules(["algoliasearch", "algoliasearch.version"]) as missing_modules: + if not missing_modules: + from .patch import patch + from .patch import unpatch + + __all__ = ["patch", "unpatch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/algoliasearch/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/algoliasearch/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f8bda3089adf1a6ce6282a3502603c80c0e4e28f GIT binary patch literal 1094 zcmY*YO;6k~5OwyWKnms5LvPkzU@J(VA4o)iN?9tQs)zy=G*xBH+M6|UoM1b<>{e|L z?Y-(B=)Hf5Yft=zo;pdousc$m8E4*j-uUI++?V4y$%Ap5f#3x^<^UFk zLc|f8+<2}V1g?w0Iy#9lV>yCj5vPf^IHeY9Qz?s#+i0;l2BG79jDQoH1-LB*R~Bz# zDwF88Z*QmAUfkGgZ7t!A?Snn?diVWSvw#T2_!S^Zs3(-Lq}YS>{nqX&+24M%JD%&6 zv=$o$*UOCSL1PJ19huEf4Qj0x=b7c^I636t^B!*m_-=o7={9Ax@fj%YB&s8#temO) zW!zlX^Kd!{7_~GAMq~5Z1;U$P|6!q>NXgrl6b%u}uk6CdVt^-IZo6FPx06z1Fa$XN zjFV1zT?GOs3KCjy5=$Zv9|;8)WV^!Pm<});X{k_fqsj;?dK~9HVPO{xlTcsc1v$I!J8h&d9q3-v&msZ15FuE7Ughh@Jxbv;E0OWK5l^O+UWc%=ZPIM<+e z6wb%O7!dO4>^pFd1c4Ir8|mr1V!r(S>Ze-tsO5f-YQK|62Hp6>Re$|l^)FV|E}rPs zrz%|`SCLv@A3j^jElT}kKNab+P{u;d<=!w|9}Hsd|Nkv+-}%r%O#TF8pMW|+qQpxF a*V7y2l(usEW)_y(quJ^Mr{>H$vz33VqgIFj literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/algoliasearch/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/algoliasearch/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..868b29ccf46a3f27899d9586cb6693bc9dcdf1c5 GIT binary patch literal 3618 zcmZ`*OLH5?5#F7B09Y(Q5Cr(p!-8d75)uiNB&hy>s3yn`PzK z_*@CeuM}2c)vpzLfz7i8XfLuww#3e{^Xvk<$lha@SdG2UF0&8VhwLMENtH@w0F{i z8+4+;cVo}x?VWWvCi8S{v-$9s>unY9H~H`o9m+i@aRsW5k| zQ5ww;MV+LiaWhqYUIbQI{WDqabAQhj-twNym$##Uc|2Yo-deu96~64=ywknSZ+#s0 zZ#u6!;oZB#pWVc-(_d~a_x=72Ka7PN1UL4E{k!}7J#U#YDerL$IuzXBT0SGrvXrsW z-%qVi13wn+sMr4tZy%#9ra}}B7*$fWalcMz$zk5MI|zgmdwkDtduh2H1%cNV&WnM^ z_Z{KA5^o7dy~wH!o#XF}hTZ^K0JwJvx0%vZger*87&*}R1v%CQVH(p90PMmLCNo~E zBV1wbD*QLlW_Ar|GlF!pEGu%zmner8j=T9IcrM}3CAY!9hc*YRzGoK8!vg}Fz@D&@ z*(d7~wOf#@bUYnPy|B*b;gjAMYROK4U{6`*I@n(98h(~4`vSrV5a{C2QB~# zV`zZc6sCbGSE1Db0IL8E(lxsoW*mSo3LA3X;32dKl#pdu7EGQUfnO3vqI7c;&J#1| zreh-H)}F z8H7@AAH$sV6jQy~ptG^YKwm$F*;Mbl9gh}vd=Wd~$-r_joJZ^XTLa<6j))xY!N}Y5 z=)zh0j^Eh{pa@z$I}wLIxJ5x1dp`KchyxJ(q33Kz+_~k5{eDE3{(I)OoQY`((C2VB z-1Wl_KzTc&MVCi|kU4=XoFMGbnQ+kaxZifR{XhU_XcVNDph0YVhmO>M(nq89=oXB_WCSI~85$ zAxYFDbz~$4f0P)9+L01JJ(*98uHH46Da^x6Vu)-vcSMBM&9lsF?OXNtmq%t|9@;{> zzr^eo<2Tc_&%pnGqI{`rDt|&34J*4d{4dDOvWa$BV!4DcYe_khnE+A}UCburKxO$| zo&ODH^GlEkF#Cv%GD+rJm0uC%BqQbyi0oGmt4GSJ^40ZUlTmh*OER!S79_C}i`lz5 z{)0TLa5$gjj?~1$eB;Fr)7y>muvdPv*EdNX?o-5*a3$=%1#1`Bj95(athfXj46c@3 zrBhtrNUmAQRXX81D>-TavKh=t%EQ-I4L-~*gyZr~T=U~v8zQgAYGTK$y>$0$zCfag zYGXim27bJQ#BCn+B-2$~cvWr$J$NsnKz3Yz#_2bt%Wj+w4A<(lo+ox9){x8jiPzF` zQ|Ytl4EF$rv2ex+jE9x8+m8JkM0AK3GLX|;42c!Kac?+&q797YsUguk_JKhq}W#WA>pmf!OpH);8V;s_u)y_lNe)&qFT^5JZOAB(mfeA@M;u}8&6 zL9^&tP(w81h3*#UlbSMv;i)lhv&fc*YfaM+jw!|16>HE-^#`ri2FfHHc#$067L43p zA2#Dva;(e}Zc&Q;t0^WD%Dvf}1#h8LCv_Xzw%!c`KSp#L}KJ=2E-4 zw)ynoy)~KK@b_^II#Y;rr*rkjBxT|S+{h3Ez(H!n{lFIzcT{&=#`q^Vn}68rM_jCN z9&wqg$N2PN8?PIO3y2&mO2i`pV2 TM3$Ca$W{p$;s5;qFw6e|K>g76 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/algoliasearch/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/algoliasearch/patch.py new file mode 100644 index 000000000..6bb88e209 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/algoliasearch/patch.py @@ -0,0 +1,152 @@ +from ddtrace import config +from ddtrace.ext import SpanTypes +from ddtrace.pin import Pin +from ddtrace.utils.wrappers import unwrap as _u +from ddtrace.vendor.wrapt import wrap_function_wrapper as _w + +from .. import trace_utils +from ...constants import SPAN_MEASURED_KEY + + +DD_PATCH_ATTR = "_datadog_patch" + +SERVICE_NAME = "algoliasearch" +APP_NAME = "algoliasearch" + +try: + import algoliasearch + from algoliasearch.version import VERSION + + algoliasearch_version = tuple([int(i) for i in VERSION.split(".")]) + + # Default configuration + config._add("algoliasearch", dict(_default_service=SERVICE_NAME, collect_query_text=False)) +except ImportError: + algoliasearch_version = (0, 0) + + +def patch(): + if algoliasearch_version == (0, 0): + return + + if getattr(algoliasearch, DD_PATCH_ATTR, False): + return + + setattr(algoliasearch, "_datadog_patch", True) + + pin = Pin(app=APP_NAME) + + if algoliasearch_version < (2, 0) and algoliasearch_version >= (1, 0): + _w(algoliasearch.index, "Index.search", _patched_search) + pin.onto(algoliasearch.index.Index) + elif algoliasearch_version >= (2, 0) and algoliasearch_version < (3, 0): + from algoliasearch import search_index + + _w(algoliasearch, "search_index.SearchIndex.search", _patched_search) + pin.onto(search_index.SearchIndex) + else: + return + + +def unpatch(): + if algoliasearch_version == (0, 0): + return + + if getattr(algoliasearch, DD_PATCH_ATTR, False): + setattr(algoliasearch, DD_PATCH_ATTR, False) + + if algoliasearch_version < (2, 0) and algoliasearch_version >= (1, 0): + _u(algoliasearch.index.Index, "search") + elif algoliasearch_version >= (2, 0) and algoliasearch_version < (3, 0): + from algoliasearch import search_index + + _u(search_index.SearchIndex, "search") + else: + return + + +# DEV: this maps serves the dual purpose of enumerating the algoliasearch.search() query_args that +# will be sent along as tags, as well as converting arguments names into tag names compliant with +# tag naming recommendations set out here: https://docs.datadoghq.com/tagging/ +QUERY_ARGS_DD_TAG_MAP = { + "page": "page", + "hitsPerPage": "hits_per_page", + "attributesToRetrieve": "attributes_to_retrieve", + "attributesToHighlight": "attributes_to_highlight", + "attributesToSnippet": "attributes_to_snippet", + "minWordSizefor1Typo": "min_word_size_for_1_typo", + "minWordSizefor2Typos": "min_word_size_for_2_typos", + "getRankingInfo": "get_ranking_info", + "aroundLatLng": "around_lat_lng", + "numericFilters": "numeric_filters", + "tagFilters": "tag_filters", + "queryType": "query_type", + "optionalWords": "optional_words", + "distinct": "distinct", +} + + +def _patched_search(func, instance, wrapt_args, wrapt_kwargs): + """ + wrapt_args is called the way it is to distinguish it from the 'args' + argument to the algoliasearch.index.Index.search() method. + """ + + if algoliasearch_version < (2, 0) and algoliasearch_version >= (1, 0): + function_query_arg_name = "args" + elif algoliasearch_version >= (2, 0) and algoliasearch_version < (3, 0): + function_query_arg_name = "request_options" + else: + return func(*wrapt_args, **wrapt_kwargs) + + pin = Pin.get_from(instance) + if not pin or not pin.enabled(): + return func(*wrapt_args, **wrapt_kwargs) + + with pin.tracer.trace( + "algoliasearch.search", + service=trace_utils.ext_service(pin, config.algoliasearch), + span_type=SpanTypes.HTTP, + ) as span: + span.set_tag(SPAN_MEASURED_KEY) + + if not span.sampled: + return func(*wrapt_args, **wrapt_kwargs) + + if config.algoliasearch.collect_query_text: + span.set_tag("query.text", wrapt_kwargs.get("query", wrapt_args[0])) + + query_args = wrapt_kwargs.get(function_query_arg_name, wrapt_args[1] if len(wrapt_args) > 1 else None) + + if query_args and isinstance(query_args, dict): + for query_arg, tag_name in QUERY_ARGS_DD_TAG_MAP.items(): + value = query_args.get(query_arg) + if value is not None: + span.set_tag("query.args.{}".format(tag_name), value) + + # Result would look like this + # { + # 'hits': [ + # { + # .... your search results ... + # } + # ], + # 'processingTimeMS': 1, + # 'nbHits': 1, + # 'hitsPerPage': 20, + # 'exhaustiveNbHits': true, + # 'params': 'query=xxx', + # 'nbPages': 1, + # 'query': 'xxx', + # 'page': 0 + # } + result = func(*wrapt_args, **wrapt_kwargs) + + if isinstance(result, dict): + if result.get("processingTimeMS", None) is not None: + span.set_metric("processing_time_ms", int(result["processingTimeMS"])) + + if result.get("nbHits", None) is not None: + span.set_metric("number_of_hits", int(result["nbHits"])) + + return result diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aredis/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aredis/__init__.py new file mode 100644 index 000000000..9d765dd68 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aredis/__init__.py @@ -0,0 +1,58 @@ +""" +The aredis integration traces aredis requests. + + +Enabling +~~~~~~~~ + +The aredis integration is enabled automatically when using +:ref:`ddtrace-run` or :ref:`patch_all()`. + +Or use :ref:`patch()` to manually enable the integration:: + + from ddtrace import patch + patch(aredis=True) + + +Global Configuration +~~~~~~~~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.aredis["service"] + + The service name reported by default for aredis traces. + + This option can also be set with the ``DD_AREDIS_SERVICE`` environment + variable. + + Default: ``"redis"`` + + +Instance Configuration +~~~~~~~~~~~~~~~~~~~~~~ + +To configure particular aredis instances use the :ref:`Pin` API:: + + import aredis + from ddtrace import Pin + + client = aredis.StrictRedis(host="localhost", port=6379) + + # Override service name for this instance + Pin.override(client, service="my-custom-queue") + + # Traces reported for this client will now have "my-custom-queue" + # as the service name. + async def example(): + await client.get("my-key") +""" + +from ...utils.importlib import require_modules + + +required_modules = ["aredis", "aredis.client"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + + __all__ = ["patch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aredis/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aredis/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1bc37d65cfbcff2320d220906796ac932e03e69d GIT binary patch literal 1501 zcma)6&2Hm15SILtWQ7Fj1N1<+Kw$%GDcfz+T8M)#HffPdcbm9Ba8XFKNZZ^}q)JkD zoI@}B0E@mr@B2!*?rC44rw&Qk2wE&q0um{4=9`)C8}f&vqbPuJ`q!WM({2#FvmXz~ zBl!3l9{mlxNH7gZNLoBBThmq;kT&ToTGKXd(e~@`&BxOY?dY#am+bLw*`0R5Vt)}% z_kIe#2)+-lgP#L(a1#t3;eICwqREmXtSDg`F=6OJVZ)?AM&W|$?T(_q*HjxFM^SVs z@QgFDi2i6s^dSs@q}G@cgli*9KrS%n8?;_hfog3*Qbp%!PKb*)RJAy3*04O!kyOZA zRM-^D47~e;v)%eUhlE}zfT9o0wkF_jBr36}T~wb7GEne?O4Bro5RAE!C2C?LR#s9O zofsx~RX2B+yZN?bU{*rWc^ z(^RckL3_Wtgsd*jA0%)|k)k#y;BvM>gwAoz4VnW#jd)%#-~(!zEP;AiIUx%y5awE< znZ+|`&CJqqkmnZ{*|Y1*i>sUL=JNW-tLK+_4uY+ilA@%-SfmwJ%qr+{FMQY(<$2Gw zOfS!Yhby5C76ttuOjs@^68Xw&MS)@kT~TwqD}*&@+R19w@*VJ!i8FXW)MqcRwyHHk zc@93PBe;2y3(lZ0G;T)3@-y(G6tx zz703eAiO`eR{>KrDlL!FPlG`~55^rHrx?9h)(rtLkk{o#--44mjJdmE^I@N=)fZ<9)~6*e@1rcLIO6RLD_ z_aqt3#QN3A>8sDwlc%CO$!-^7G`joz#EwOkOp=OKLnj^Q!_{3i+H6XiG*`iHhL(k9 nnI*o=B+Hm!Cd=Y#bANE=$vC$~>qB^TP7d1#ttjk-hpm4AE8yg^ literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aredis/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/aredis/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0675ccf47bb17412c23f503474578d4e0f5a7550 GIT binary patch literal 4330 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 VALUE_MAX_LEN: + cmd = cmd[:VALUE_MAX_LEN] + VALUE_TOO_LONG_MARK + + if length + len(cmd) > CMD_MAX_LEN: + prefix = cmd[: CMD_MAX_LEN - length] + out.append("%s%s" % (prefix, VALUE_TOO_LONG_MARK)) + break + + out.append(cmd) + length += len(cmd) + except Exception: + out.append(VALUE_PLACEHOLDER) + break + + return " ".join(out) + + +def _get_tags(conn): + return _extract_conn_tags(conn.connection_pool.connection_kwargs) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asgi/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asgi/__init__.py new file mode 100644 index 000000000..b3d9981b8 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asgi/__init__.py @@ -0,0 +1,75 @@ +""" +The asgi__ middleware for tracing all requests to an ASGI-compliant application. + +To configure tracing manually:: + + from ddtrace.contrib.asgi import TraceMiddleware + + # app = + app = TraceMiddleware(app) + +Then use ddtrace-run when serving your application. For example, if serving with Uvicorn:: + + ddtrace-run uvicorn app:app + +If using Python 3.6, the legacy ``AsyncioContextProvider`` will have to be +enabled before using the middleware:: + + from ddtrace.contrib.asyncio.provider import AsyncioContextProvider + from ddtrace import tracer # Or whichever tracer instance you plan to use + tracer.configure(context_provider=AsyncioContextProvider()) + +The middleware also supports using a custom function for handling exceptions for a trace:: + + from ddtrace.contrib.asgi import TraceMiddleware + + def custom_handle_exception_span(exc, span): + span.set_tag("http.status_code", 501) + + # app = + app = TraceMiddleware(app, handle_exception_span=custom_handle_exception_span) + + +To retrieve the request span from the scope of an ASGI request use the ``span_from_scope`` +function:: + + from ddtrace.contrib.asgi import span_from_scope + + def handle_request(scope, send): + span = span_from_scope(scope) + if span: + span.set_tag(...) + ... + + +Configuration +~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.asgi['distributed_tracing'] + + Whether to use distributed tracing headers from requests received by your Asgi app. + + Default: ``True`` + +.. py:data:: ddtrace.config.asgi['service_name'] + + The service name reported for your ASGI app. + + Can also be configured via the ``DD_SERVICE`` environment variable. + + Default: ``'asgi'`` + +.. __: https://asgi.readthedocs.io/ +""" + +from ...utils.importlib import require_modules + + +required_modules = [] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .middleware import TraceMiddleware + from .middleware import span_from_scope + + __all__ = ["TraceMiddleware", "span_from_scope"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asgi/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asgi/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..50a4d485f4edb2f1c42d5488c5396c61bc398fc7 GIT binary patch literal 2235 zcmbVO&2HO95T<1Lk8qkk!ZtZrDs0kj8Uq&MAg-MjKD2dXr72Vts}*-7Z9p#BU6QtZ z=%o)(^aXnFEBREQr@TT>omo#sFDT(9DK zy}yp@HMu@+_BY;mPrdKFSKbev+_>;Mo8qrE&+~f|V8V_S=PXfD#&9VNuu*DQZbYQ? zn29)M27WHU=9c9t6Pg`ge08=PrAZbmp>rlOTtp&QsSbRL ztc|-JOlbm|6&7X@cTJ($C2d+TB^6mYyj2JL5+Q&qfi=PoQ=_@%rOGGlb*ZA%=vg&& zAbD<+3Oj#BqT`+kB-?^(krH)qwX;bg9Xg*8n=>A;VovMwuDn(-2 zl-VL9zStTJ!J@+E$gNSKBO;`u@fX}0F zn+e^<#~ntiPS;^T{HMpjLe6tBZahB-;L=I7KeglrNYTWUxUHvDZhC6>ie^kmne6j zOJ!1>0G=tOFp9+PP669=N^I99aNcD!JhmH#v>zCR2R+L)vVlrN|J@3not?(bLxM^f z;7KZr80@=7y}xu|km(X-ObboTD&D^7Y~HMMj?OvnKP++;V-R;-#cF7pXwm<;jw5G* z`VSLjN&e>>{z9x5uE{T+kDUMM8T?%+tA&-dsWXJIeQRJQAcIF5JA>+EnkO(3j~srfHyFA~uq7jmw&$~h0R e>CMKo3a00TlKlv8truG>t)|~-HMSaC&3^#{hTOvd literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asgi/__pycache__/middleware.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asgi/__pycache__/middleware.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..888995b141e74ab4bfb7bc8d9176dce8e8880e4c GIT binary patch literal 4919 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 JWmy?3`(N?B(ux28 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asgi/__pycache__/utils.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asgi/__pycache__/utils.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bf24d090077e2d9b673cf6f02567ed6bba0da064 GIT binary patch literal 3145 zcmbVOL2u*66(%LwRv0h3P1+*44bsaB&`Mr!5nwmLqO?R?EM!t8DLeK>pg3ch8;TTW zMs{U5Coj-mi{69en4glrpleV43qAFlp=8T$ivlG;a)z9F?|a|(X5I{+J+lm4Uw;4B z_~%{2_`B+CUKMnHk6Y~FL5*{RnzWLc=cayF&MUM^YafjBD&3;nc-QC-t>e8#E1}W& zDE_c*7*?m4&SII4(=3$>axu>nnHISqS1hLmXOvvMa%IJi+*_WvCVd~Oi3#)3%cJYoL{ z&KkxF)&B)YDFUc#Q?y9tQ^C>Vri9SIn1sEy$P=Bn@4IlLgU>0eOV!AZA)fB!wmj(<$H~`O1Z& zr4j2~RzQ(bYeQnj5|vt*o2rE4Dyi}^y@XmUh#~5^AqoA%=+q7zg7zTrPu#B4B?o6D z>N%w256%Mjs27o*-|sp>NNlf*uU-_m2gAt6hcE09Grv$jEZaLH&NqX=2}9xs#62GL zT^NJ)!1f~737f?AI{jhS^^TeZ28icJr0*WP5r##6Q#)NvvmT}q|BxIzL8k{R_JP}X zqcaWU(2YFh>k!UtGO&Zl?F{>NKnBBL;D-)DSXS2!JAKLfZGuMlpAQ%oJ*Y_Hb_Y|xkZW~j(nq1%0p&||Z;7l1+ z9BA&F>{CQQtjsypE&L_{z$u8#)b0hx<1gf&>hSoY^!7qd)?jfFIg8|D(BUpagE;oav<*F@bfAWiA*>#RX~viD$EHRRah%+n8i*5+{q7LM}2! zUeIDb&e+Q&&axPd#^(5zwt4>Rk*b6vfzpt%(R$?J`g>{N{(H2nk921=YV0g`Ca48c z^5ssNt4%|qp*C_MmOGcRD1XIbktb=f{1m?mj%_B**}8snbvF3PTz_T7+?P> z+Sq=su&cJOV%|pm!UiVVH?P`n#`*R8y)WPI^H;C)+1}_+ll;w_o8RrJYcgv`?O8f| z2|yB2zr4Dcy;&@#tWBxpaq{q9RcU<=XaiIxTC>ISlT?hJg#1Ej`zt(*tzENb;;vZg zuBsM)hVPG3LPbN`kN#{fTJM-3roYkAKl#|D~(8V3PJyD9-;gP$k777uB zk4dT1y`rSZSt(?qoUSjGldAtW+~PAl{(`)`Grz0eR&UMQ%3a09L9C~8%k(tcjoNYx z^32%z4rd8Np;*=gs>!mhiOdtmKY|%OmgA6TEPs)+>(Pd69py^-HO#tsv%exy7Ed#) z^>tPt%f!3Z69XlV3Rj0o{!8^xX;8Y=9zXxDHs4`j>3eIp#y>&tktAxBDp@o{rKDPw zcP4)>P0b2=iiw+LDNFn<6J%AJ?+I5W^{GZ1(d+YCoL$EYb7F2bn(~Ijt zG`%`oWQa<=JJ;0SIQ+jU6GKSistz1$gLp(LXb r-$9=%;q_W5J*Y9IeA>f Optional[Span] + """Retrieve the top-level ASGI span from the scope.""" + return scope.get("datadog", {}).get("request_span") + + +class TraceMiddleware: + """ + ASGI application middleware that traces the requests. + + Args: + app: The ASGI application. + tracer: Custom tracer. Defaults to the global tracer. + """ + + def __init__( + self, + app, + tracer=None, + integration_config=config.asgi, + handle_exception_span=_default_handle_exception_span, + span_modifier=None, + ): + self.app = guarantee_single_callable(app) + self.tracer = tracer or ddtrace.tracer + self.integration_config = integration_config + self.handle_exception_span = handle_exception_span + self.span_modifier = span_modifier + + async def __call__(self, scope, receive, send): + if scope["type"] != "http": + return await self.app(scope, receive, send) + + try: + headers = _extract_headers(scope) + except Exception: + log.warning("failed to decode headers for distributed tracing", exc_info=True) + headers = {} + else: + trace_utils.activate_distributed_headers( + self.tracer, int_config=self.integration_config, request_headers=headers + ) + + resource = "{} {}".format(scope["method"], scope["path"]) + + span = self.tracer.trace( + name=self.integration_config.get("request_span_name", "asgi.request"), + service=trace_utils.int_service(None, self.integration_config), + resource=resource, + span_type=SpanTypes.WEB, + ) + + scope["datadog"] = {"request_span": span} + + if self.span_modifier: + self.span_modifier(span, scope) + + sample_rate = self.integration_config.get_analytics_sample_rate(use_global_config=True) + if sample_rate is not None: + span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, sample_rate) + + method = scope.get("method") + server = scope.get("server") + if server and len(server) == 2: + port = server[1] + server_host = server[0] + (":" + str(port) if port is not None and port != 80 else "") + full_path = scope.get("root_path", "") + scope.get("path", "") + url = scope.get("scheme", "http") + "://" + server_host + full_path + else: + url = None + + if self.integration_config.trace_query_string: + query_string = scope.get("query_string") + if len(query_string) > 0: + query_string = bytes_to_str(query_string) + else: + query_string = None + + trace_utils.set_http_meta( + span, self.integration_config, method=method, url=url, query=query_string, request_headers=headers + ) + + tags = _extract_versions_from_scope(scope, self.integration_config) + span.set_tags(tags) + + async def wrapped_send(message): + if span and message.get("type") == "http.response.start" and "status" in message: + status_code = message["status"] + else: + status_code = None + + if "headers" in message: + response_headers = message["headers"] + else: + response_headers = None + + trace_utils.set_http_meta( + span, self.integration_config, status_code=status_code, response_headers=response_headers + ) + + return await send(message) + + try: + return await self.app(scope, receive, wrapped_send) + except Exception as exc: + (exc_type, exc_val, exc_tb) = sys.exc_info() + span.set_exc_info(exc_type, exc_val, exc_tb) + self.handle_exception_span(exc, span) + reraise(exc_type, exc_val, exc_tb) + finally: + try: + del scope["datadog"]["request_span"] + except KeyError: + pass + span.finish() diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asgi/utils.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asgi/utils.py new file mode 100644 index 000000000..0551352dc --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asgi/utils.py @@ -0,0 +1,82 @@ +""" +Compatibility functions vendored from asgiref + +Source: https://github.com/django/asgiref +Version: 3.2.10 +License: + +Copyright (c) Django Software Foundation and individual contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of Django nor the names of its contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +""" +import asyncio +import inspect + + +def is_double_callable(application): + """ + Tests to see if an application is a legacy-style (double-callable) application. + """ + # Look for a hint on the object first + if getattr(application, "_asgi_single_callable", False): + return False + if getattr(application, "_asgi_double_callable", False): + return True + # Uninstanted classes are double-callable + if inspect.isclass(application): + return True + # Instanted classes depend on their __call__ + if hasattr(application, "__call__"): + # We only check to see if its __call__ is a coroutine function - + # if it's not, it still might be a coroutine function itself. + if asyncio.iscoroutinefunction(application.__call__): + return False + # Non-classes we just check directly + return not asyncio.iscoroutinefunction(application) + + +def double_to_single_callable(application): + """ + Transforms a double-callable ASGI application into a single-callable one. + """ + + async def new_application(scope, receive, send): + instance = application(scope) + return await instance(receive, send) + + return new_application + + +def guarantee_single_callable(application): + """ + Takes either a single- or double-callable application and always returns it + in single-callable style. Use this to add backwards compatibility for ASGI + 2.0 applications to your server/test harness/etc. + """ + if is_double_callable(application): + application = double_to_single_callable(application) + return application diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/__init__.py new file mode 100644 index 000000000..b1cb683d1 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/__init__.py @@ -0,0 +1,62 @@ +""" +This integration provides automatic instrumentation to trace the execution flow +of concurrent execution of ``asyncio.Task``. Also it provides a legacy context +provider for supporting tracing of asynchronous execution in Python < 3.7. + +For asynchronous execution tracing in Python < 3.7 to work properly the tracer must +be configured as follows:: + + import asyncio + from ddtrace import tracer + from ddtrace.contrib.asyncio import context_provider + + # enable asyncio support + tracer.configure(context_provider=context_provider) + + async def some_work(): + with tracer.trace('asyncio.some_work'): + # do something + + # launch your coroutines as usual + loop = asyncio.get_event_loop() + loop.run_until_complete(some_work()) + loop.close() + +In addition, helpers are provided to simplify how the tracing ``Context`` is +handled between scheduled coroutines and ``Future`` invoked in separated +threads: + + * ``set_call_context(task, ctx)``: attach the context to the given ``Task`` + so that it will be available from the ``tracer.get_call_context()`` + * ``ensure_future(coro_or_future, *, loop=None)``: wrapper for the + ``asyncio.ensure_future`` that attaches the current context to a new + ``Task`` instance + * ``run_in_executor(loop, executor, func, *args)``: wrapper for the + ``loop.run_in_executor`` that attaches the current context to the new + thread so that the trace can be resumed regardless when it's executed + * ``create_task(coro)``: creates a new asyncio ``Task`` that inherits the + current active ``Context`` so that generated traces in the new task are + attached to the main trace +""" +from ...utils.importlib import require_modules + + +required_modules = ["asyncio"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from ...internal.compat import CONTEXTVARS_IS_AVAILABLE + from ...provider import DefaultContextProvider + from .provider import AsyncioContextProvider + + if CONTEXTVARS_IS_AVAILABLE: + context_provider = DefaultContextProvider() + else: + context_provider = AsyncioContextProvider() + + from .helpers import ensure_future + from .helpers import run_in_executor + from .helpers import set_call_context + from .patch import patch + + __all__ = ["context_provider", "set_call_context", "ensure_future", "run_in_executor", "patch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ed79243e9fc5c209003f0018483a30de355e564e GIT binary patch literal 2453 zcma)8&u`l{6qX&wag-lUcPMrm2=q|9NbPjpIwWX^WnBvdD6+1}x7f7`NFPjMt;}M!?(Vly}|*QK;H{9iOn;ksJADwCN?+VZ0S>`Edy2?P%MN zH)J>OMqLJT=q_tw>?? zL@;g%EuGF`E`eq1Oqp}#DiNoNGc@7EWt{MHo|Jy)Sn6r0$0X4zDUAW~rA3$@ji{Ze zMCidOwQokF0XdXb6XBL@63J7V%ut8p=Ps<#7&6v|*s>_JaYCiuK0ZOMSDG2EbZM6i z3PqmJTm~LJB=-lO4Z`p#sQ4FQbO+U4+eEy^rXEDtaYMw|`mz|Gaw(TV5LschXM zT+uVh=ho)&@@gv!sIUUtyRPHW^@)C^)te@akBQYek1-s*{(;wt4o-#3DmIvbx6!*@ zgB>j7Z!gK?o?C=zugzr;^f`D*OO<3~rb`1cH5y2zkcO6&wxrTqlv)?$(cHm7%3aJS zK(rVadi{mWz?3R3l@l^fbY4jAcy9rx*IyD$q_!M(hDVA}#so5XmtjXnh+UR7Cec^-XY+Z z(Xlr5>n^#oOE5`~jD15yg6u0BjMRWGo5U4$5Fd}Tat~|lB1L;K3%A) zT=o5ns-!Z3NRb^v#Z~LlriZ|G>)xTwE*S$80WmeH{ZFX4&r@wl+5bg~4qRfb@(e{$ zxrvKD2VA<3>0i-a3l&R!$QK1OoX>qR-K$Tyam=3NxN3K0jy&F<=?h+UorIVnQc# zin4f^gzs12@a#7(x`uyW3LsLhv1DwtRyPz_`|$DC$EQzToxVJLaS|V$#D^~rkDeWV z`Roa--TaD=X(?Te?|F5OnfHxK?ajmL@VT}O8@H}7%_F$V5187yUUo3IT2Pl{7dtim z7Twt3=YUzmTL*9W?Qq<`aj_Q1Oeb+1b)mIMJE(fU6lbOh646fWH(NN~&4snlT`ni^ zpf-w1=;Jw9HyE<0Gd~7Q3(di)9+(YWSgYIE5*7~)0U;XfBJm%A*~T4s`)f8VjFhWlq~`g-rd>yOR7`>NQB&r`L(|MrtTywYNLIxIwS7rzrg=iSNM zVt+Qv`LI6a7Eg>69B0Eip~D!eP`EfA6tj!Y!z$mOW4`Q1aBKB8SK7_6(Q0hsui0vJ Kn(d|^!`t661s#C^ literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/__pycache__/compat.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/__pycache__/compat.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e73df6c4e9095649abacac933b2c9e836d2b82e2 GIT binary patch literal 551 zcmZuvy-ve05I!e`mWZDPiG_g)(V@8f2&oc6NQkuqEdsh!ZX6&bb)vIdNLF|OUI5Xt zuauRES773tR%#_U%jf&QEP@UMECA`e zI=C%eS$czxqbhP~YDw0D>hd@3hGxF%4-o@px<=?H-})A%Lg0d?-X)cJ20qysZlDDv zFwT>QI}Gf&qkCI$Tj&XER^7ZTbgKL?Mu!sf)vKX0;UKs~jBx_EOwL06Jo5y@kuU)t z3FP;2Bm>kuJK&9;dhXXx`iFRMq>_5~T;SBqOPN_}(EZEQ4POUP9i*q-~(Dp1~v#h-QD6}vJi}-(udF6Bv%V89p(N_R% L3$D1H1^c6Ns%hr>7Teeb>Ry=TYAqX6TR zzx|c}>L>{QMGyNU#>3~BCc{Mpvp|F*D#BUlexq3=V$mz&WpCCC1KjtE{&FxI;C>(u zz71vvBEsMBhe+HJqi>_x5Ko6cgyKjXRW{2q$v>tOuO?cYb!MQ+$q ziB%z4UZ%xLNW(ZQa$}jwSgOivS?01#C0k0nP{J^~;C7Vql3hx+GEy+Bn8}w_k!Krb z)KW54NzHApO2gKR9FiBj6h&UnN9U@v^2Rbev#%?T4S9)wWA$n&@k+3Z3vM=LnyZTo zoG#?q=tm$DJpD1mbsRSDKlZO%NndGooeQb)=H0dCRV77|YRRokEH}^7h(2xy-S;rh zG0i);*q{zG$Zhuia7Z(FhF|l%;Fo}qPQB#m z^EKDvL8_J&0KUwN+-}_Iq>&SFaq-gtLGj05J#oI0I1)CcEA5Wwsf8u`CZ0J-Psed{ zV5H^NYEAJuJ5J8Od^-K&n`!cRI{ouFYI>x46Q}m(AAufX%YSYrmCmn$+vJ+-Nu~<; z#!PNMnmoEJ*H;fex%#dC=yzrHFu9qRj~?BA{E)`HnoK8EUOk{l;6?G^`nGzs*(~Km z2&;Lz`y~iE4;}CJC)pb=Ay^qN5XWMJR*!kHTSi z5{^Q*4kbyclwyzu`RxFcMEnuce27aOxBiOj*!inp2e0Bfw7ohaPu2a`gBOE(0AGdt zmk`ytD_fOF09F1Uep`@Z+JyZn&;1V`BcGN`E-Sm~Tno3_#eNth1`NqudLp5SXI)5hJ9;O+-0-|q zYr|9th}XmwYi^u2q-Q}T2ldf&GL^`k4J62VeDFy}BWLUh!3X_!@9#T#@Jj|hq^d!@ zPvZUO4+;gM+-H1d5D$EgYU{on_w_My%B7tq9&PR-PLjM#yw{a(P7*4&+vo9tBa&vg z-O!`N^4Z_xdT#VRk~3%-3k-3z#VI`+`E+^O^V!a6bgwZCn9!0w%lHxTVm zL^k2LZ5!moV8QTAg12{>_=f0$`ULE1CA!TN!~RSsW93>BEUB_3)MC>Tc{Dr4D)kph z)%HZqu;Y$#uZgl!zYoN`Pl-O?rr>w6_8F#eh>wm!O)Dwpb`bVdDZj)tKgY!aebmOt zqDo%HA|xNY^!4!jH{l1DtB bXH)`R3)|}tqhWLu9>v4M)1#9QhA008Scso* literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6a8556296734fc72f4d7af3f93dc5701cb2f3461 GIT binary patch literal 1219 zcmZ`(OK;Oa5Z<+u*m0adDQ|J`fde@ZmxmCQ5JDh0Ri%O=AzzH+-KGwXZOv}dCW2G= z1N;F5$NnW>IrT4aVrCN{7gpNwyfn{m{jS^f7@kMpz9x^GjQu2G^H?Z6!>bOF2qt*W zqScp29AzuFCU#_V(%Hy2q9*bU(ac*DCvuT*2`6t)yvRe|740u9>IhGC@aqfymi7H} zjLi1?yzaanj%BJ3pgf(Uc;y6=Dn6PPsm{tGCaIEeZlgC^ThoFx=X@n@ugoG&K_*(p zI#C}}3jF7gUjy$8yy`s?%`_KWSW6o&3i~b7mXRAvY)0E-Y~dq=L*3MT+!8JA7(M^) z{$QV(ZhvB9Pj{A&5JZEYe%g$wwkNjpGi0gCq5k&ySs~rJ9I`>confL(Lgo~*L{U%B!g;RHw{N+sf$Qh!{-g|TORM(F84WHN8SHHbAZ+j(Cn=Ng8*6s z#zw&GtpU?mTBzmU9Rn8^;nLRL(jq&;#aKlX!O~jXej_L447Z9sWW^{rf^vdeCjg0H zQi^FV)c{;f{tDVi%~<#Q9q>u$66q1SK!n=DT}|NtR_QZ&!@(71u^qA3rh*4&junmQ?&`z0|JuIXm;sH$(okveF_@?)>=&|9qK{ zzj1J~)L`%}RQ&)NK?IG7PlO}fn2sIaq1gAJ@A|Io*J5v6^J|oRN5qn-za*kA9qAr= z{*s_uq}zB4a>&M87uqf@ErM56m-_P9q@|PCw#?i*#76JdF;b1g5>Mj3*%q zI9a@EpnrglBt8|ycLeoa;rO0tzaj%FJW+eu^lM_pDn&t=j-79aCd}8xdv??k%P?xZ zA|COZpzMX>@_ju^!YDnXBL9mL@{}ihC^a)9$#`g@sbpbk2^*w}@$w{-2Qr+PC{5Pb z6UE1JKUI4z1|>?gQIoMu498`V!wrix+nGeMVK|XyEFD<+{eC5`-)C{Oqqv&gV0x5J zVqs-Xw1jUyD~oHMhbw%3gS#egMn0lUnS~BsW+ZZ6U3v$Q%#a2 zN`^27KcQ1toTk~D72;nE0#R};sGPSEA~zQW$%Pf?^b=@u@|xt%5q;t0j=pNBadL9# zymoVXL@CLE^5?G2vLH2y3YLGmIUZfDIKO6*mcrk zBoHoz6J-rt*wF89bG_H^uf2h2Kr_0{q8SXO32;!<^dyr?ErFgz!*UW3E9w<@3J(t# z?Mi51a8%b4_-9nH5IqFsA#8GSpk=od15V_DM(r5GE{v58sfC5M=wy%?ZAJB z&Og2XztOw4KqhqXR8_E#Ha>)^uR$~KTC^f< z12+k!!fyn>6HGG5q_Xmi`WUyK!T1Wy-Gi!eo;Yoc(h_6fa0{OKm2+?|)#=iW<6o<2 z1VI8X)F3EYK`>6mB*uO_2!5OJxLT=0eow<7uv*)6QJRYGR;Ul;y)u=BGLB*2aj;qC zEjKWqVrtMBxWI^I;KWL+-RW3HcI#z8Hx`LW;mbl{fI$w0FBS`s-Fia$JLVOQyQS{m Pz&8cacuxbMfyez9yfKc( literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/__pycache__/wrappers.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/__pycache__/wrappers.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a5a75d4352fdda1c87f369f38b024974ccc6fca0 GIT binary patch literal 1129 zcmZuxO>fgM7|vHeRN7rYaPiU>v)a7C23bKr=2-5QKtYMGTjtkPF@o0;J|xp;Yh`$6%8n6SOCd zaH!~-v=EFS*i+Ua+;}mPgL(u93xXaR&8bAQj6n!z2=HRurx|5b&hM z1i@j=xim(&>{i15gFUR68P9i+LlhH8o?_i7P!C;ReMpmovXCIE4rL`B+>lB3{2mRV zInT)&E;`2&F|PF0WjIN;K#s;S5r8}0YDg1v)vXEL-DLw(W2JVhv_ILzY%sV!?duVz{>V>hdTbIW$?^I+ z9c;HN;zyAw{Qat%LeQzde|r9ovXH%WTRzEH{pSphid9%e9Vz5lLjiAuU0(D67pLA2Bdz(ZM*IM1C``Q!2kdN literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/compat.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/compat.py new file mode 100644 index 000000000..104154e37 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/compat.py @@ -0,0 +1,16 @@ +import asyncio + + +if hasattr(asyncio, "current_task"): + + def asyncio_current_task(): + try: + return asyncio.current_task() + except RuntimeError: + return None + + +else: + + def asyncio_current_task(): + return asyncio.Task.current_task() diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/helpers.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/helpers.py new file mode 100644 index 000000000..f1803ee40 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/helpers.py @@ -0,0 +1,81 @@ +""" +This module includes a list of convenience methods that +can be used to simplify some operations while handling +Context and Spans in instrumented ``asyncio`` code. +""" +import asyncio + +import ddtrace + +from .provider import AsyncioContextProvider +from .wrappers import wrapped_create_task + + +def set_call_context(task, ctx): + """ + Updates the ``Context`` for the given Task. Useful when you need to + pass the context among different tasks. + + This method is available for backward-compatibility. Use the + ``AsyncioContextProvider`` API to set the current active ``Context``. + """ + setattr(task, AsyncioContextProvider._CONTEXT_ATTR, ctx) + + +def ensure_future(coro_or_future, *, loop=None, tracer=None): + """Wrapper that sets a context to the newly created Task. + + If the current task already has a Context, it will be attached to the new Task so the Trace list will be preserved. + """ + tracer = tracer or ddtrace.tracer + current_ctx = tracer.current_trace_context() + task = asyncio.ensure_future(coro_or_future, loop=loop) + set_call_context(task, current_ctx) + return task + + +def run_in_executor(loop, executor, func, *args, tracer=None): + """Wrapper function that sets a context to the newly created Thread. + + If the current task has a Context, it will be attached as an empty Context with the current_span activated to + inherit the ``trace_id`` and the ``parent_id``. + + Because the Executor can run the Thread immediately or after the + coroutine is executed, we may have two different scenarios: + * the Context is copied in the new Thread and the trace is sent twice + * the coroutine flushes the Context and when the Thread copies the + Context it is already empty (so it will be a root Span) + + To support both situations, we create a new Context that knows only what was + the latest active Span when the new thread was created. In this new thread, + we fallback to the thread-local ``Context`` storage. + + """ + tracer = tracer or ddtrace.tracer + current_ctx = tracer.current_trace_context() + + # prepare the future using an executor wrapper + future = loop.run_in_executor(executor, _wrap_executor, func, args, tracer, current_ctx) + return future + + +def _wrap_executor(fn, args, tracer, ctx): + """ + This function is executed in the newly created Thread so the right + ``Context`` can be set in the thread-local storage. This operation + is safe because the ``Context`` class is thread-safe and can be + updated concurrently. + """ + # the AsyncioContextProvider knows that this is a new thread + # so it is legit to pass the Context in the thread-local storage; + # fn() will be executed outside the asyncio loop as a synchronous code + tracer.context_provider.activate(ctx) + return fn(*args) + + +def create_task(*args, **kwargs): + """This function spawns a task with a Context that inherits the + `trace_id` and the `parent_id` from the current active one if available. + """ + loop = asyncio.get_event_loop() + return wrapped_create_task(loop.create_task, None, args, kwargs) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/patch.py new file mode 100644 index 000000000..257576709 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/patch.py @@ -0,0 +1,40 @@ +import asyncio +import sys + +from ddtrace.vendor.wrapt import ObjectProxy +from ddtrace.vendor.wrapt import wrap_function_wrapper as _w + +from ...utils.wrappers import unwrap as _u +from .wrappers import wrapped_create_task + + +def patch(): + """Patches current loop `create_task()` method to enable spawned tasks to + parent to the base task context. + """ + if getattr(asyncio, "_datadog_patch", False): + return + setattr(asyncio, "_datadog_patch", True) + + if sys.version_info < (3, 7, 0): + _w(asyncio.BaseEventLoop, "create_task", wrapped_create_task) + + # also patch event loop if not inheriting the wrapped create_task from BaseEventLoop + loop = asyncio.get_event_loop() + if not isinstance(loop.create_task, ObjectProxy): + _w(loop, "create_task", wrapped_create_task) + + +def unpatch(): + """Remove tracing from patched modules.""" + + if getattr(asyncio, "_datadog_patch", False): + setattr(asyncio, "_datadog_patch", False) + + if sys.version_info < (3, 7, 0): + _u(asyncio.BaseEventLoop, "create_task") + + # also unpatch event loop if not inheriting the already unwrapped create_task from BaseEventLoop + loop = asyncio.get_event_loop() + if isinstance(loop.create_task, ObjectProxy): + _u(loop, "create_task") diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/provider.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/provider.py new file mode 100644 index 000000000..356753c1e --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/provider.py @@ -0,0 +1,74 @@ +import asyncio + +from ...provider import BaseContextProvider +from ...provider import DatadogContextMixin +from ...span import Span + + +class AsyncioContextProvider(BaseContextProvider, DatadogContextMixin): + """Manages the active context for asyncio execution. Framework + instrumentation that is built on top of the ``asyncio`` library, should + use this provider when contextvars are not available (Python versions + less than 3.7). + + This Context Provider inherits from ``DefaultContextProvider`` because + it uses a thread-local storage when the ``Context`` is propagated to + a different thread, than the one that is running the async loop. + """ + + # Task attribute used to set/get the context + _CONTEXT_ATTR = "__datadog_context" + + def activate(self, context, loop=None): + """Sets the scoped ``Context`` for the current running ``Task``.""" + loop = self._get_loop(loop) + if not loop: + super(AsyncioContextProvider, self).activate(context) + return context + + # the current unit of work (if tasks are used) + task = asyncio.Task.current_task(loop=loop) + if task: + setattr(task, self._CONTEXT_ATTR, context) + return context + + def _get_loop(self, loop=None): + """Helper to try and resolve the current loop""" + try: + return loop or asyncio.get_event_loop() + except RuntimeError: + # Detects if a loop is available in the current thread; + # DEV: This happens when a new thread is created from the out that is running the async loop + # DEV: It's possible that a different Executor is handling a different Thread that + # works with blocking code. In that case, we fallback to a thread-local Context. + pass + return None + + def _has_active_context(self, loop=None): + """Helper to determine if we have a currently active context""" + loop = self._get_loop(loop=loop) + if loop is None: + return super(AsyncioContextProvider, self)._has_active_context() + + # the current unit of work (if tasks are used) + task = asyncio.Task.current_task(loop=loop) + if task is None: + return False + + ctx = getattr(task, self._CONTEXT_ATTR, None) + return ctx is not None + + def active(self, loop=None): + """Returns the active context for the execution.""" + loop = self._get_loop(loop=loop) + if not loop: + return super(AsyncioContextProvider, self).active() + + # the current unit of work (if tasks are used) + task = asyncio.Task.current_task(loop=loop) + if task is None: + return None + ctx = getattr(task, self._CONTEXT_ATTR, None) + if isinstance(ctx, Span): + return self._update_active(ctx) + return ctx diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/wrappers.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/wrappers.py new file mode 100644 index 000000000..ddbabc4ec --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/asyncio/wrappers.py @@ -0,0 +1,25 @@ +from .compat import asyncio_current_task +from .provider import AsyncioContextProvider + + +def wrapped_create_task(wrapped, instance, args, kwargs): + """Wrapper for ``create_task(coro)`` that propagates the current active + ``Context`` to the new ``Task``. This function is useful to connect traces + of detached executions. + + Note: we can't just link the task contexts due to the following scenario: + * begin task A + * task A starts task B1..B10 + * finish task B1-B9 (B10 still on trace stack) + * task A starts task C + * now task C gets parented to task B10 since it's still on the stack, + however was not actually triggered by B10 + """ + new_task = wrapped(*args, **kwargs) + current_task = asyncio_current_task() + + ctx = getattr(current_task, AsyncioContextProvider._CONTEXT_ATTR, None) + if ctx: + setattr(new_task, AsyncioContextProvider._CONTEXT_ATTR, ctx) + + return new_task diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/boto/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/boto/__init__.py new file mode 100644 index 000000000..cb3daee37 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/boto/__init__.py @@ -0,0 +1,26 @@ +""" +Boto integration will trace all AWS calls made via boto2. +This integration is automatically patched when using ``patch_all()``:: + + import boto.ec2 + from ddtrace import patch + + # If not patched yet, you can patch boto specifically + patch(boto=True) + + # This will report spans with the default instrumentation + ec2 = boto.ec2.connect_to_region("us-west-2") + # Example of instrumented query + ec2.get_all_instances() +""" + +from ...utils.importlib import require_modules + + +required_modules = ["boto.connection"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + + __all__ = ["patch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/boto/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/boto/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4a00a3c6a8e3b909dad95161d94c7e6dfa9be6d8 GIT binary patch literal 865 zcmY*X!EVz)5M4V-Q><3$2XI)mmslck8=z7uiU7fZ3lg-b$d}E=J8`zM*Y55*sdME6 z5I?{ji8CM5E2sVfC&rtmDkFKlGqdw%-rN1WyBiR+;~(GoFQ1S`-7^wy z^WPpJ!JEPsR4NOp=2jMpE@hTct9b&Hpv8BK6wXwC)Dh2(UKC8J?*K9}adN_2T; ziyU#9ikg<(CUX#UIR`~6BUMT#6BlHd?}wAg$w?4UG?|x$whnm+$;icKy2z;z>rw6w zxZ-um0ewHCs<8K6YOqgeT~s)(S_d5-H68^@Gf z85G3MshtBAFymEbk+ZS7%0XGDn8U=O>FNFa!=z9O63cACG^8kNf4?$=B^Wyx?T4=7 zfWEutd6@w%W)C1yuB!@ky+IwOU>mhq)0L|POg{{6Tlfk>@202Ws*)O5UWh6Kv+6ah zHr=u9EnabRu;V!EHhLez?oF35Q6!ATy~@hW4A<&ra;ov|k$CS8R%~$NUM@{@f17|q zYG9hl*iWLnSm&0%i>TCc!7W4!uA^Cz3D7259Yy1*T3#L=Uyk(AQ&k?a>r{=$t7nIe zrDb#;m9iW-mRUAftjcj+=MddJfcrlxYqgw44bh09D$+7GEbE)?7i$q-Hk8c{F73UY M)|MZ5ZEwf_2b(boX8-^I literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/boto/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/boto/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..418697696112b3c75cbdb316adddc8d3038871d8 GIT binary patch literal 3658 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 Y2$(l+Ek|rzMhGWIw+psy->^UbFF#0kQ2+n{ literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/boto/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/boto/patch.py new file mode 100644 index 000000000..f3aa77689 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/boto/patch.py @@ -0,0 +1,176 @@ +import inspect + +import boto.connection + +from ddtrace import config +from ddtrace.constants import ANALYTICS_SAMPLE_RATE_KEY +from ddtrace.constants import SPAN_MEASURED_KEY +from ddtrace.ext import SpanTypes +from ddtrace.ext import aws +from ddtrace.ext import http +from ddtrace.pin import Pin +from ddtrace.utils.wrappers import unwrap +from ddtrace.vendor import wrapt + +from ...utils import get_argument_value + + +# Original boto client class +_Boto_client = boto.connection.AWSQueryConnection + +AWS_QUERY_ARGS_NAME = ("operation_name", "params", "path", "verb") +AWS_AUTH_ARGS_NAME = ( + "method", + "path", + "headers", + "data", + "host", + "auth_path", + "sender", +) +AWS_QUERY_TRACED_ARGS = {"operation_name", "params", "path"} +AWS_AUTH_TRACED_ARGS = {"path", "data", "host"} + + +def patch(): + if getattr(boto.connection, "_datadog_patch", False): + return + setattr(boto.connection, "_datadog_patch", True) + + # AWSQueryConnection and AWSAuthConnection are two different classes called by + # different services for connection. + # For example EC2 uses AWSQueryConnection and S3 uses AWSAuthConnection + wrapt.wrap_function_wrapper("boto.connection", "AWSQueryConnection.make_request", patched_query_request) + wrapt.wrap_function_wrapper("boto.connection", "AWSAuthConnection.make_request", patched_auth_request) + Pin(service="aws", app="aws").onto(boto.connection.AWSQueryConnection) + Pin(service="aws", app="aws").onto(boto.connection.AWSAuthConnection) + + +def unpatch(): + if getattr(boto.connection, "_datadog_patch", False): + setattr(boto.connection, "_datadog_patch", False) + unwrap(boto.connection.AWSQueryConnection, "make_request") + unwrap(boto.connection.AWSAuthConnection, "make_request") + + +# ec2, sqs, kinesis +def patched_query_request(original_func, instance, args, kwargs): + + pin = Pin.get_from(instance) + if not pin or not pin.enabled(): + return original_func(*args, **kwargs) + + endpoint_name = getattr(instance, "host").split(".")[0] + + with pin.tracer.trace( + "{}.command".format(endpoint_name), + service="{}.{}".format(pin.service, endpoint_name), + span_type=SpanTypes.HTTP, + ) as span: + span.set_tag(SPAN_MEASURED_KEY) + + operation_name = None + if args: + operation_name = get_argument_value(args, kwargs, 0, "action") + span.resource = "%s.%s" % (endpoint_name, operation_name.lower()) + else: + span.resource = endpoint_name + + aws.add_span_arg_tags(span, endpoint_name, args, AWS_QUERY_ARGS_NAME, AWS_QUERY_TRACED_ARGS) + + # Obtaining region name + region_name = _get_instance_region_name(instance) + + meta = { + aws.AGENT: "boto", + aws.OPERATION: operation_name, + } + if region_name: + meta[aws.REGION] = region_name + + span.set_tags(meta) + + # Original func returns a boto.connection.HTTPResponse object + result = original_func(*args, **kwargs) + span.set_tag(http.STATUS_CODE, getattr(result, "status")) + span.set_tag(http.METHOD, getattr(result, "_method")) + + # set analytics sample rate + span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, config.boto.get_analytics_sample_rate()) + + return result + + +# s3, lambda +def patched_auth_request(original_func, instance, args, kwargs): + + # Catching the name of the operation that called make_request() + operation_name = None + + # Go up the stack until we get the first non-ddtrace module + # DEV: For `lambda.list_functions()` this should be: + # - ddtrace.contrib.boto.patch + # - ddtrace.vendor.wrapt.wrappers + # - boto.awslambda.layer1 (make_request) + # - boto.awslambda.layer1 (list_functions) + # But can vary depending on Python versions; that's why we use an heuristic + frame = inspect.currentframe().f_back + operation_name = None + while frame: + if frame.f_code.co_name == "make_request": + operation_name = frame.f_back.f_code.co_name + break + frame = frame.f_back + + pin = Pin.get_from(instance) + if not pin or not pin.enabled(): + return original_func(*args, **kwargs) + + endpoint_name = getattr(instance, "host").split(".")[0] + + with pin.tracer.trace( + "{}.command".format(endpoint_name), + service="{}.{}".format(pin.service, endpoint_name), + span_type=SpanTypes.HTTP, + ) as span: + span.set_tag(SPAN_MEASURED_KEY) + if args: + http_method = get_argument_value(args, kwargs, 0, "method") + span.resource = "%s.%s" % (endpoint_name, http_method.lower()) + else: + span.resource = endpoint_name + + aws.add_span_arg_tags(span, endpoint_name, args, AWS_AUTH_ARGS_NAME, AWS_AUTH_TRACED_ARGS) + + # Obtaining region name + region_name = _get_instance_region_name(instance) + + meta = { + aws.AGENT: "boto", + aws.OPERATION: operation_name, + } + if region_name: + meta[aws.REGION] = region_name + + span.set_tags(meta) + + # Original func returns a boto.connection.HTTPResponse object + result = original_func(*args, **kwargs) + span.set_tag(http.STATUS_CODE, getattr(result, "status")) + span.set_tag(http.METHOD, getattr(result, "_method")) + + # set analytics sample rate + span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, config.boto.get_analytics_sample_rate()) + + return result + + +def _get_instance_region_name(instance): + region = getattr(instance, "region", None) + + if not region: + return None + if isinstance(region, str): + return region.split(":")[1] + else: + return region.name diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/botocore/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/botocore/__init__.py new file mode 100644 index 000000000..c60c8fa80 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/botocore/__init__.py @@ -0,0 +1,59 @@ +""" +The Botocore integration will trace all AWS calls made with the botocore +library. Libraries like Boto3 that use Botocore will also be patched. + +Enabling +~~~~~~~~ + +The botocore integration is enabled automatically when using +:ref:`ddtrace-run` or :ref:`patch_all()`. + +Or use :ref:`patch()` to manually enable the integration:: + + from ddtrace import patch + patch(botocore=True) + +Configuration +~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.botocore['distributed_tracing'] + + Whether to inject distributed tracing data to requests in SQS and Lambda. + + Can also be enabled with the ``DD_BOTOCORE_DISTRIBUTED_TRACING`` environment variable. + + Default: ``True`` + +.. py:data:: ddtrace.config.botocore['invoke_with_legacy_context'] + + This preserves legacy behavior when tracing directly invoked Python and Node Lambda + functions instrumented with datadog-lambda-python < v41 or datadog-lambda-js < v3.58.0. + + Legacy support for older libraries is available with + ``ddtrace.config.botocore.invoke_with_legacy_context = True`` or by setting the environment + variable ``DD_BOTOCORE_INVOKE_WITH_LEGACY_CONTEXT=true``. + + + Default: ``False`` + + +Example:: + + from ddtrace import config + + # Enable distributed tracing + config.botocore['distributed_tracing'] = True + +""" + + +from ...utils.importlib import require_modules + + +required_modules = ["botocore.client"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + + __all__ = ["patch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/botocore/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/botocore/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7e0320c1d99b9323d4ba93458306fc75fbb9d388 GIT binary patch literal 1744 zcma)6O>f&a7?!`1R!ET@d)R@)4y_NClBEq6BMFw+Zf7j8*H~RT7X?L&w9QnaG>UR! zA9mRf81@Ht-(Sjgr~ZYV_L8&$uh@nXutb^f=kq)-Uq5=}I-Geh z#9YkWpV!MZC6;OaQ6kR~)8>pKQm8b6!gYyDbTelHvgt5w$)@d)QqxIG7UFolhwW*I zQi-gEsQ`ob)8_I0{xpP&hSC(iVOE-eze)iQQJAc@7K6$LknOhXB3M(ICTN`-@gz&7 zvTd7Flk#cV!brbLl;yA13_#=|QEy?sJ?t|Xrn ziczdF08E~|GZkIU84xIS3tU{YNTCf|!#vm`9bcPB_EW)fm4huBy&54RD7qlYgc5)V z+UpPjIIcO-8EN60|1#65z zkaM!&APu9VHEMv41du>oHY`QoFV!3dMI$jtVJ0jbmh3`AdWtk$fPbO80+kv-G@>+} zwPJ%_E3;9K(c;;@mg7gq*Exct1Mm6Q-kya6UDzt~A~PbIf>Rn(VA77Do;>J^EC`P^ zh$gG4DlF&VW0`pWjTAaT7Ia7j@$-6^Z0A!XOb_7Vm=`6PmKt2 zf3@!)O~lRh-r@CEa{oYNd-!%Hj*eE(_Vk)%e&A<3YiVUd%+_L+9W9p$^VgTt{aeop nTpfPdsE;uhTw&~G%ex217Kl^bcD@6T`u8OT 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 JFwS8%{|7)^&tL!m literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/botocore/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/botocore/patch.py new file mode 100644 index 000000000..475fd778f --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/botocore/patch.py @@ -0,0 +1,177 @@ +""" +Trace queries to aws api done via botocore client +""" +# 3p +import base64 +import json + +import botocore.client + +from ddtrace import config +from ddtrace.vendor import wrapt + +# project +from ...constants import ANALYTICS_SAMPLE_RATE_KEY +from ...constants import SPAN_MEASURED_KEY +from ...ext import SpanTypes +from ...ext import aws +from ...ext import http +from ...internal.logger import get_logger +from ...pin import Pin +from ...propagation.http import HTTPPropagator +from ...utils import get_argument_value +from ...utils.formats import asbool +from ...utils.formats import deep_getattr +from ...utils.formats import get_env +from ...utils.wrappers import unwrap + + +# Original botocore client class +_Botocore_client = botocore.client.BaseClient + +ARGS_NAME = ("action", "params", "path", "verb") +TRACED_ARGS = {"params", "path", "verb"} + +log = get_logger(__name__) + +# Botocore default settings +config._add( + "botocore", + { + "distributed_tracing": asbool(get_env("botocore", "distributed_tracing", default=True)), + "invoke_with_legacy_context": asbool(get_env("botocore", "invoke_with_legacy_context", default=False)), + }, +) + + +def inject_trace_data_to_message_attributes(trace_data, entry): + if "MessageAttributes" not in entry: + entry["MessageAttributes"] = {} + # An Amazon SQS message can contain up to 10 metadata attributes. + if len(entry["MessageAttributes"]) < 10: + entry["MessageAttributes"]["_datadog"] = {"DataType": "String", "StringValue": json.dumps(trace_data)} + else: + log.debug("skipping trace injection, max number (10) of MessageAttributes exceeded") + + +def inject_trace_to_sqs_batch_message(args, span): + trace_data = {} + HTTPPropagator.inject(span.context, trace_data) + params = args[1] + + for entry in params["Entries"]: + inject_trace_data_to_message_attributes(trace_data, entry) + + +def inject_trace_to_sqs_message(args, span): + trace_data = {} + HTTPPropagator.inject(span.context, trace_data) + params = args[1] + + inject_trace_data_to_message_attributes(trace_data, params) + + +def modify_client_context(client_context_object, trace_headers): + if config.botocore["invoke_with_legacy_context"]: + trace_headers = {"_datadog": trace_headers} + + if "custom" in client_context_object: + client_context_object["custom"].update(trace_headers) + else: + client_context_object["custom"] = trace_headers + + +def inject_trace_to_client_context(args, span): + trace_headers = {} + HTTPPropagator.inject(span.context, trace_headers) + client_context_object = {} + params = args[1] + if "ClientContext" in params: + try: + client_context_json = base64.b64decode(params["ClientContext"]).decode("utf-8") + client_context_object = json.loads(client_context_json) + except Exception: + log.warning("malformed client_context=%s", params["ClientContext"], exc_info=True) + return + modify_client_context(client_context_object, trace_headers) + try: + json_context = json.dumps(client_context_object).encode("utf-8") + except Exception: + log.warning("unable to encode modified client context as json: %s", client_context_object, exc_info=True) + return + params["ClientContext"] = base64.b64encode(json_context).decode("utf-8") + + +def patch(): + if getattr(botocore.client, "_datadog_patch", False): + return + setattr(botocore.client, "_datadog_patch", True) + + wrapt.wrap_function_wrapper("botocore.client", "BaseClient._make_api_call", patched_api_call) + Pin(service="aws", app="aws").onto(botocore.client.BaseClient) + + +def unpatch(): + if getattr(botocore.client, "_datadog_patch", False): + setattr(botocore.client, "_datadog_patch", False) + unwrap(botocore.client.BaseClient, "_make_api_call") + + +def patched_api_call(original_func, instance, args, kwargs): + + pin = Pin.get_from(instance) + if not pin or not pin.enabled(): + return original_func(*args, **kwargs) + + endpoint_name = deep_getattr(instance, "_endpoint._endpoint_prefix") + + with pin.tracer.trace( + "{}.command".format(endpoint_name), service="{}.{}".format(pin.service, endpoint_name), span_type=SpanTypes.HTTP + ) as span: + span.set_tag(SPAN_MEASURED_KEY) + operation = None + if args: + operation = get_argument_value(args, kwargs, 0, "operation_name") + # DEV: join is the fastest way of concatenating strings that is compatible + # across Python versions (see + # https://stackoverflow.com/questions/1316887/what-is-the-most-efficient-string-concatenation-method-in-python) + span.resource = ".".join((endpoint_name, operation.lower())) + + if config.botocore["distributed_tracing"]: + if endpoint_name == "lambda" and operation == "Invoke": + inject_trace_to_client_context(args, span) + if endpoint_name == "sqs" and operation == "SendMessage": + inject_trace_to_sqs_message(args, span) + if endpoint_name == "sqs" and operation == "SendMessageBatch": + inject_trace_to_sqs_batch_message(args, span) + + else: + span.resource = endpoint_name + + aws.add_span_arg_tags(span, endpoint_name, args, ARGS_NAME, TRACED_ARGS) + + region_name = deep_getattr(instance, "meta.region_name") + + span._set_str_tag("aws.agent", "botocore") + if operation is not None: + span._set_str_tag("aws.operation", operation) + if region_name is not None: + span._set_str_tag("aws.region", region_name) + + result = original_func(*args, **kwargs) + + response_meta = result.get("ResponseMetadata") + if response_meta: + if "HTTPStatusCode" in response_meta: + span.set_tag(http.STATUS_CODE, response_meta["HTTPStatusCode"]) + + if "RetryAttempts" in response_meta: + span.set_tag("retry_attempts", response_meta["RetryAttempts"]) + + if "RequestId" in response_meta: + span.set_tag("aws.requestid", response_meta["RequestId"]) + + # set analytics sample rate + span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, config.botocore.get_analytics_sample_rate()) + + return result diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/bottle/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/bottle/__init__.py new file mode 100644 index 000000000..78d5562af --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/bottle/__init__.py @@ -0,0 +1,46 @@ +""" +The bottle integration traces the Bottle web framework. Add the following +plugin to your app:: + + import bottle + from ddtrace import tracer + from ddtrace.contrib.bottle import TracePlugin + + app = bottle.Bottle() + plugin = TracePlugin(service="my-web-app") + app.install(plugin) + +:ref:`All HTTP tags ` are supported for this integration. + +Configuration +~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.bottle['distributed_tracing'] + + Whether to parse distributed tracing headers from requests received by your bottle app. + + Can also be enabled with the ``DD_BOTTLE_DISTRIBUTED_TRACING`` environment variable. + + Default: ``True`` + + +Example:: + + from ddtrace import config + + # Enable distributed tracing + config.bottle['distributed_tracing'] = True + +""" + +from ...utils.importlib import require_modules + + +required_modules = ["bottle"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + from .trace import TracePlugin + + __all__ = ["TracePlugin", "patch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/bottle/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/bottle/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..729b4a7a59396facd66b470faeb470a5d42665dc GIT binary patch literal 1254 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 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/bottle/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/bottle/patch.py new file mode 100644 index 000000000..7ec581357 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/bottle/patch.py @@ -0,0 +1,35 @@ +import bottle + +from ddtrace import config +from ddtrace.vendor import wrapt + +from ...utils.formats import asbool +from ...utils.formats import get_env +from .trace import TracePlugin + + +# Configure default configuration +config._add( + "bottle", + dict( + distributed_tracing=asbool(get_env("bottle", "distributed_tracing", default=True)), + ), +) + + +def patch(): + """Patch the bottle.Bottle class""" + if getattr(bottle, "_datadog_patch", False): + return + + setattr(bottle, "_datadog_patch", True) + wrapt.wrap_function_wrapper("bottle", "Bottle.__init__", traced_init) + + +def traced_init(wrapped, instance, args, kwargs): + wrapped(*args, **kwargs) + + service = config._get_service(default="bottle") + + plugin = TracePlugin(service=service) + instance.install(plugin) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/bottle/trace.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/bottle/trace.py new file mode 100644 index 000000000..e0b5950a6 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/bottle/trace.py @@ -0,0 +1,95 @@ +from bottle import HTTPError +from bottle import HTTPResponse +from bottle import request +from bottle import response + +import ddtrace +from ddtrace import config + +from .. import trace_utils +from ...constants import ANALYTICS_SAMPLE_RATE_KEY +from ...constants import SPAN_MEASURED_KEY +from ...ext import SpanTypes +from ...utils.formats import asbool + + +class TracePlugin(object): + name = "trace" + api = 2 + + def __init__(self, service="bottle", tracer=None, distributed_tracing=None): + self.service = config.service or service + self.tracer = tracer or ddtrace.tracer + if distributed_tracing is not None: + config.bottle.distributed_tracing = distributed_tracing + + @property + def distributed_tracing(self): + return config.bottle.distributed_tracing + + @distributed_tracing.setter + def distributed_tracing(self, distributed_tracing): + config.bottle["distributed_tracing"] = asbool(distributed_tracing) + + def apply(self, callback, route): + def wrapped(*args, **kwargs): + if not self.tracer or not self.tracer.enabled: + return callback(*args, **kwargs) + + resource = "{} {}".format(request.method, route.rule) + + trace_utils.activate_distributed_headers( + self.tracer, int_config=config.bottle, request_headers=request.headers + ) + + with self.tracer.trace( + "bottle.request", + service=self.service, + resource=resource, + span_type=SpanTypes.WEB, + ) as s: + s.set_tag(SPAN_MEASURED_KEY) + # set analytics sample rate with global config enabled + s.set_tag(ANALYTICS_SAMPLE_RATE_KEY, config.bottle.get_analytics_sample_rate(use_global_config=True)) + + code = None + result = None + try: + result = callback(*args, **kwargs) + return result + except (HTTPError, HTTPResponse) as e: + # you can interrupt flows using abort(status_code, 'message')... + # we need to respect the defined status_code. + # we also need to handle when response is raised as is the + # case with a 4xx status + code = e.status_code + raise + except Exception: + # bottle doesn't always translate unhandled exceptions, so + # we mark it here. + code = 500 + raise + finally: + if isinstance(result, HTTPResponse): + response_code = result.status_code + elif code: + response_code = code + else: + # bottle local response has not yet been updated so this + # will be default + response_code = response.status_code + + method = request.method + url = request.urlparts._replace(query="").geturl() + trace_utils.set_http_meta( + s, + config.bottle, + method=method, + url=url, + status_code=response_code, + query=request.query_string, + request_headers=request.headers, + response_headers=response.headers, + ) + + return wrapped diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cassandra/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cassandra/__init__.py new file mode 100644 index 000000000..f0718e1a8 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cassandra/__init__.py @@ -0,0 +1,37 @@ +"""Instrument Cassandra to report Cassandra queries. + +``patch_all`` will automatically patch your Cluster instance to make it work. +:: + + from ddtrace import Pin, patch + from cassandra.cluster import Cluster + + # If not patched yet, you can patch cassandra specifically + patch(cassandra=True) + + # This will report spans with the default instrumentation + cluster = Cluster(contact_points=["127.0.0.1"], port=9042) + session = cluster.connect("my_keyspace") + # Example of instrumented query + session.execute("select id from my_table limit 10;") + + # Use a pin to specify metadata related to this cluster + cluster = Cluster(contact_points=['10.1.1.3', '10.1.1.4', '10.1.1.5'], port=9042) + Pin.override(cluster, service='cassandra-backend') + session = cluster.connect("my_keyspace") + session.execute("select id from my_table limit 10;") +""" +from ...utils.importlib import require_modules + + +required_modules = ["cassandra.cluster"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .session import get_traced_cassandra + from .session import patch + + __all__ = [ + "get_traced_cassandra", + "patch", + ] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cassandra/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cassandra/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3d9151252cc7c94c27aebf1d3880279de76b23a6 GIT binary patch literal 1365 zcmbVM&2Aev5azCA%N88CEehmH$OlP9R3MExhAnhTSqD>vN*rK4R z>~)TLfFduDdtarG;A>BLg&vB|{wV@_X-i_c{26{T^UVxj+`HE&*dD!m%YW|@@<+MY zZu_|S3P1ZC2SFktJmD)Z@gv_OqOjy~nqaxDy}E~N&t)NJp)%E3sm zLBD@-k#QGaGOpCc1-+I^QJ%Xr;ZDZ5SJ3L17HMwiQhDub!bqpnR!mofzN3TAHiK9$YwIgbl?S7Z!2I0gFm930FQu*BZb-5j*X548r0x=^fF;kBt9-7h zMP^c60k<~KTV_UasvVCV%TlSGo&P+To_-!ouuliSpz|1n`D}7_Ilp;tqA;nJWov4E~oa45vr{_6Ngb z+Ro3m<`0MeBnPu4NY`MD6fkPk9a9TtEn}DuH_<#<@c0U}82%4Vn*jVbzTRVY4X`8?xdMCp@y4b$ncsW0=%*`itf~dUHbMxoQ)Q;X_kx%m9KIdz-}M+1Y0Ox3 zm^-O#P#2Gq3*)0i^iiW#v}&WnL}D80Rl9N5Yux&PTY_ROGxi%X%Tj=YTlSeO4&GdoJChTUVXS3V0N!eCecphdlJE`6PrB2qj*{moM z2%Bo({L4}>auIGE4jGeLI>v&mxZC@prtQm;BKrvr-TMcfUBB;jy#w!{<9q&Je*^Yn Bt8M@Q literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cassandra/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cassandra/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f3562917f694a42c9e90c8b4033ce67bb5955cbe GIT binary patch literal 307 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 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cassandra/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cassandra/patch.py new file mode 100644 index 000000000..d82e620f7 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cassandra/patch.py @@ -0,0 +1,5 @@ +from .session import patch +from .session import unpatch + + +__all__ = ["patch", "unpatch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cassandra/session.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cassandra/session.py new file mode 100644 index 000000000..8aab1d08f --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cassandra/session.py @@ -0,0 +1,291 @@ +""" +Trace queries along a session to a cassandra cluster +""" +import sys + +# 3p +import cassandra.cluster + +# project +from ddtrace import config + +from ...constants import ANALYTICS_SAMPLE_RATE_KEY +from ...constants import ERROR_MSG +from ...constants import ERROR_TYPE +from ...constants import SPAN_MEASURED_KEY +from ...ext import SpanTypes +from ...ext import cassandra as cassx +from ...ext import net +from ...internal.compat import maybe_stringify +from ...internal.compat import stringify +from ...internal.logger import get_logger +from ...pin import Pin +from ...utils import get_argument_value +from ...utils.deprecation import deprecated +from ...utils.formats import deep_getattr +from ...vendor import wrapt + + +log = get_logger(__name__) + +RESOURCE_MAX_LENGTH = 5000 +SERVICE = "cassandra" +CURRENT_SPAN = "_ddtrace_current_span" +PAGE_NUMBER = "_ddtrace_page_number" + +# Original connect connect function +_connect = cassandra.cluster.Cluster.connect + + +def patch(): + """patch will add tracing to the cassandra library.""" + setattr(cassandra.cluster.Cluster, "connect", wrapt.FunctionWrapper(_connect, traced_connect)) + Pin(service=SERVICE, app=SERVICE).onto(cassandra.cluster.Cluster) + + +def unpatch(): + cassandra.cluster.Cluster.connect = _connect + + +def traced_connect(func, instance, args, kwargs): + session = func(*args, **kwargs) + if not isinstance(session.execute, wrapt.FunctionWrapper): + # FIXME[matt] this should probably be private. + setattr(session, "execute_async", wrapt.FunctionWrapper(session.execute_async, traced_execute_async)) + return session + + +def _close_span_on_success(result, future): + span = getattr(future, CURRENT_SPAN, None) + if not span: + log.debug("traced_set_final_result was not able to get the current span from the ResponseFuture") + return + try: + span.set_tags(_extract_result_metas(cassandra.cluster.ResultSet(future, result))) + except Exception: + log.debug("an exception occurred while setting tags", exc_info=True) + finally: + span.finish() + delattr(future, CURRENT_SPAN) + + +def traced_set_final_result(func, instance, args, kwargs): + result = get_argument_value(args, kwargs, 0, "response") + _close_span_on_success(result, instance) + return func(*args, **kwargs) + + +def _close_span_on_error(exc, future): + span = getattr(future, CURRENT_SPAN, None) + if not span: + log.debug("traced_set_final_exception was not able to get the current span from the ResponseFuture") + return + try: + # handling the exception manually because we + # don't have an ongoing exception here + span.error = 1 + span.set_tag(ERROR_MSG, exc.args[0]) + span.set_tag(ERROR_TYPE, exc.__class__.__name__) + except Exception: + log.debug("traced_set_final_exception was not able to set the error, failed with error", exc_info=True) + finally: + span.finish() + delattr(future, CURRENT_SPAN) + + +def traced_set_final_exception(func, instance, args, kwargs): + exc = get_argument_value(args, kwargs, 0, "response") + _close_span_on_error(exc, instance) + return func(*args, **kwargs) + + +def traced_start_fetching_next_page(func, instance, args, kwargs): + has_more_pages = getattr(instance, "has_more_pages", True) + if not has_more_pages: + return func(*args, **kwargs) + session = getattr(instance, "session", None) + cluster = getattr(session, "cluster", None) + pin = Pin.get_from(cluster) + if not pin or not pin.enabled(): + return func(*args, **kwargs) + + # In case the current span is not finished we make sure to finish it + old_span = getattr(instance, CURRENT_SPAN, None) + if old_span: + log.debug("previous span was not finished before fetching next page") + old_span.finish() + + query = getattr(instance, "query", None) + + span = _start_span_and_set_tags(pin, query, session, cluster) + + page_number = getattr(instance, PAGE_NUMBER, 1) + 1 + setattr(instance, PAGE_NUMBER, page_number) + setattr(instance, CURRENT_SPAN, span) + try: + return func(*args, **kwargs) + except Exception: + with span: + span.set_exc_info(*sys.exc_info()) + raise + + +def traced_execute_async(func, instance, args, kwargs): + cluster = getattr(instance, "cluster", None) + pin = Pin.get_from(cluster) + if not pin or not pin.enabled(): + return func(*args, **kwargs) + + query = get_argument_value(args, kwargs, 0, "query") + + span = _start_span_and_set_tags(pin, query, instance, cluster) + + try: + result = func(*args, **kwargs) + setattr(result, CURRENT_SPAN, span) + setattr(result, PAGE_NUMBER, 1) + setattr(result, "_set_final_result", wrapt.FunctionWrapper(result._set_final_result, traced_set_final_result)) + setattr( + result, + "_set_final_exception", + wrapt.FunctionWrapper(result._set_final_exception, traced_set_final_exception), + ) + setattr( + result, + "start_fetching_next_page", + wrapt.FunctionWrapper(result.start_fetching_next_page, traced_start_fetching_next_page), + ) + # Since we cannot be sure that the previous methods were overwritten + # before the call ended, we add callbacks that will be run + # synchronously if the call already returned and we remove them right + # after. + result.add_callbacks( + _close_span_on_success, _close_span_on_error, callback_args=(result,), errback_args=(result,) + ) + result.clear_callbacks() + return result + except Exception: + with span: + span.set_exc_info(*sys.exc_info()) + raise + + +def _start_span_and_set_tags(pin, query, session, cluster): + service = pin.service + tracer = pin.tracer + span = tracer.trace("cassandra.query", service=service, span_type=SpanTypes.CASSANDRA) + span.set_tag(SPAN_MEASURED_KEY) + _sanitize_query(span, query) + span.set_tags(_extract_session_metas(session)) # FIXME[matt] do once? + span.set_tags(_extract_cluster_metas(cluster)) + # set analytics sample rate if enabled + span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, config.cassandra.get_analytics_sample_rate()) + return span + + +def _extract_session_metas(session): + metas = {} + + if getattr(session, "keyspace", None): + # FIXME the keyspace can be overridden explicitly in the query itself + # e.g. 'select * from trace.hash_to_resource' + metas[cassx.KEYSPACE] = session.keyspace.lower() + + return metas + + +def _extract_cluster_metas(cluster): + metas = {} + if deep_getattr(cluster, "metadata.cluster_name"): + metas[cassx.CLUSTER] = cluster.metadata.cluster_name + if getattr(cluster, "port", None): + metas[net.TARGET_PORT] = cluster.port + + return metas + + +def _extract_result_metas(result): + metas = {} + if result is None: + return metas + + future = getattr(result, "response_future", None) + + if future: + # get the host + host = maybe_stringify(getattr(future, "coordinator_host", None)) + if host: + host, _, port = host.partition(":") + metas[net.TARGET_HOST] = host + if port: + metas[net.TARGET_PORT] = int(port) + elif hasattr(future, "_current_host"): + address = deep_getattr(future, "_current_host.address") + if address: + metas[net.TARGET_HOST] = address + + query = getattr(future, "query", None) + if getattr(query, "consistency_level", None): + metas[cassx.CONSISTENCY_LEVEL] = query.consistency_level + if getattr(query, "keyspace", None): + metas[cassx.KEYSPACE] = query.keyspace.lower() + + page_number = getattr(future, PAGE_NUMBER, 1) + has_more_pages = getattr(future, "has_more_pages") + is_paginated = has_more_pages or page_number > 1 + metas[cassx.PAGINATED] = is_paginated + if is_paginated: + metas[cassx.PAGE_NUMBER] = page_number + + if hasattr(result, "current_rows"): + result_rows = result.current_rows or [] + metas[cassx.ROW_COUNT] = len(result_rows) + + return metas + + +def _sanitize_query(span, query): + # TODO (aaditya): fix this hacky type check. we need it to avoid circular imports + t = type(query).__name__ + + resource = None + if t in ("SimpleStatement", "PreparedStatement"): + # reset query if a string is available + resource = getattr(query, "query_string", query) + elif t == "BatchStatement": + resource = "BatchStatement" + # Each element in `_statements_and_parameters` is: + # (is_prepared, statement, parameters) + # ref:https://github.com/datastax/python-driver/blob/13d6d72be74f40fcef5ec0f2b3e98538b3b87459/cassandra/query.py#L844 + # + # For prepared statements, the `statement` value is just the query_id + # which is not a statement and when trying to join with other strings + # raises an error in python3 around joining bytes to unicode, so this + # just filters out prepared statements from this tag value + q = "; ".join(q[1] for q in query._statements_and_parameters[:2] if not q[0]) + span.set_tag("cassandra.query", q) + span.set_metric("cassandra.batch_size", len(query._statements_and_parameters)) + elif t == "BoundStatement": + ps = getattr(query, "prepared_statement", None) + if ps: + resource = getattr(ps, "query_string", None) + elif t == "str": + resource = query + else: + resource = "unknown-query-type" # FIXME[matt] what else do to here? + + span.resource = stringify(resource)[:RESOURCE_MAX_LENGTH] + + +# +# DEPRECATED +# + + +@deprecated(message="Use patching instead (see the docs).", version="1.0.0") +def get_traced_cassandra(*args, **kwargs): + return _get_traced_cluster(*args, **kwargs) + + +def _get_traced_cluster(*args, **kwargs): + return cassandra.cluster.Cluster diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__init__.py new file mode 100644 index 000000000..1155d74b2 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__init__.py @@ -0,0 +1,66 @@ +""" +The Celery integration will trace all tasks that are executed in the +background. Functions and class based tasks are traced only if the Celery API +is used, so calling the function directly or via the ``run()`` method will not +generate traces. However, calling ``apply()``, ``apply_async()`` and ``delay()`` +will produce tracing data. To trace your Celery application, call the patch method:: + + import celery + from ddtrace import patch + + patch(celery=True) + app = celery.Celery() + + @app.task + def my_task(): + pass + + class MyTask(app.Task): + def run(self): + pass + +Configuration +~~~~~~~~~~~~~ +.. py:data:: ddtrace.config.celery['distributed_tracing'] + + Whether or not to pass distributed tracing headers to Celery workers. + + Can also be enabled with the ``DD_CELERY_DISTRIBUTED_TRACING`` environment variable. + + Default: ``False`` + +.. py:data:: ddtrace.config.celery['producer_service_name'] + + Sets service name for producer + + Default: ``'celery-producer'`` + +.. py:data:: ddtrace.config.celery['worker_service_name'] + + Sets service name for worker + + Default: ``'celery-worker'`` + +""" +from ...utils.importlib import require_modules + + +required_modules = ["celery"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .app import patch_app + from .app import unpatch_app + from .patch import patch + from .patch import unpatch + from .task import patch_task + from .task import unpatch_task + + __all__ = [ + "patch", + "patch_app", + "patch_task", + "unpatch", + "unpatch_app", + "unpatch_task", + ] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b974e2daa5de1ce01875eb1fb75524d719e344a1 GIT binary patch literal 1780 zcma)6PjA~c6qjtre-x+b_6dkx5(9~x?oWXj>(I_gw*XtQB`&(D7${n#Z8j36k(3<; z!!En+GVBZNzQevyt~>QB^t4A(c6}KT3Z~zCeE;8%e7Uz5c(5M-^(XnK?RoziW_$I3 zc?v)EJ8aa8JnGX%>Sv9p;d`_>_oF6ju;y?6AJeGCTIv_tqC4qM){fe6-lA=wJ5k5d zowS>Eqpqd9K=-1arF&_R?MAzn4uI}QeV}(~pYG8Ibe}$)x1+sR-e=zT-lg}0M?bjo z1|JgH^1NWOU}(%zCM(2+W^+k2&jniXG(}pHgdt)mqOKLv3!)K`46z%Ql$ud60U-;f zB)Oi;yc9G<&r6XQ7YY$UQIZm+(3B`pc211Fbxct%QiwV;F4j3b`|cv(3YDOLgj9|a zh|a~_s?2K7h;qpi4Ys*NOHS-z9LrK14&oSPOfPckDiFC2=1efCs*b8c^mV>sOD2yt z&f}OAMOqo_BUCe(s7fT(p{XN|DNBh}32bi)nbQ)wV@fdzQld$SCVAboDlg@_CgYqZ zwy!RxO}HRBS=7mnMnQmJ@vO+DMv0w=mCR(GAxhmO>SJr^3@mx*^q)**$p+R6BBCd? zo6t@7aNsPS!dYl0VHGKxp{&A&KOBs#$mCJL(44xP-?!DoXc$vNY-t(?rtylUvn}y^ zC*xeq_`Gzm3Vz*QK^UT<8kx?GM(ZNO#2SRIrq>6QD=qodpbgi(KKRMz_G$rxX3~HF z#*B1s(*|&C3^v1AFhZFG0O#wOta5n`lF)h@69Fg(v`qopf=p8Y>x%0|4Xm>>JU;*C z{PIV9c5yYiy!i6vRaEP_bj$CU=EabOR>%FSO(;sS`K936bzJs z>#q$AFX3JG%xmY#uGuwc+;uRfd4h4YUuvGJ(A~6@Ph|r%qK9>^bd&6U z#+8D5zY#PcQ5V8Gwh>L7sAV&-aZNN63f?q~|Mp}bc)Pv385WW+;gK2wfQGX?HEFMpKCuYry;bd6w;+PAi!RPVvwm7Y-j1B7tWAhJp v%3PwM>+ld`F1W@xEULTi=dS->n6|5rU~3)qo9#y6xBUM5ui5Z7-@E?+WnLrS literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__pycache__/app.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__pycache__/app.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..85f09d80f04124fdff1f5693761752efc6dd4d3d GIT binary patch literal 1764 zcmZ{kOK;mo5XbqFNKw?=eI$K=R|SaWv_VhpMCskDpQW1iAY)GgLh+C$xDZPb0<=ACPub$FNet~J)>tykJ`@E-HihI(P= z(JTq$9Hn_0C3ve((qtOVPtuj6hqj&`Z7gm8R%_Xd8Td!P^fhhfE>4T7PIi47lZZ$~P@Op|@ z{wEr#ZL!kaxH6&3%~zV#m2X{Pt+#siSQ^q?SiE*mlQpI4Z_Quyt$tJAYJXsFq`hz! z4e4@&+iwl!H_`X@{1*D{J->r~chB#k-{12G=pWqrTMaAjY~Gwm8BXTDoC_aI?N8z` z&wZJyK$vB5Gzn#tCVrT3ADfVfiFAD&=?eTg24ewg{ZoPJ(yuyqsMZvnAmE`4c{&TS zP)_F0$FJGoB87A8PcC2`VYiZSDTXGqE59&t*TN2N8_ui?5uOh}D(sn%p_G7hlmdur zu+ShLYIi{THD;xWOo19wG-a5d2W9&TyGkECn)1r2venR}(4ZouoGI~v2F>(Kq&}E% zc5kT&0y>6;t5zvQpoM{V26XVo%q&XYY4ADfzw=QB(K?i3v<_i3O=FH#9Bm$rj?a>d z#nF?+SMccTBs&Uzo+Zb}o5x2qX4&ZVD2uX(QIg9rjvuZ!+41G&Qj9oPT;ARBLnTCK zqp~U^Ea+i&Su|7&0`$6=L++!|EKj#|&#dc?Zs^uO^_pvT_1zD?#5EH-WVeILN+nmE zU!&M@p6_#x+#|EbjY%{U*W5gBz&Fy`BJWqkbA4goII^KwuPxBw>H%pwTSIG;{wS8|THJB0YLrZbKZSJppD^<%|6iWfLmv>0??Fxp-4bnR!auGK3i1BSU1t zeKLd;d`5;yD$N(DismZ0Yw!aMWM literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__pycache__/constants.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__pycache__/constants.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8849e0e360488beabc5095a48bb63fb610e2066a GIT binary patch literal 663 zcmY*X&5qhI5RM6d3xP#jweP@z4ck^LA+4%LY*baO041R8F_ZZv5hBiu#*xVlFIuNBVHPEPSJ4&F)B%$;=i$!W z*F8IFSf=)ojHC&N>LFR2A7W3)glfDMq83GdrPFfNMg!lY8O}b;GRFO`q!(M^6C{Aef)XJ2ZQ2ue{V}+2eybrFXCxN z;<&dh#30Yt%svk0DXQ1ZWOQ#I#%n981UySGMZT-wtktk%_D20`M*2cIt6VnANH3(K HMi%-5cWS|v literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ee10f11c84e0a903ba438cee1d188f9144c6dc27 GIT binary patch literal 1321 zcmb7EOKTiQ5T2QRzx0d@u`i7=1c9|9KM)8)5Udms20N@}2j(*Bovt0V+|x63_pH3S zCf6Kt55_+F;vckEpY#_DHm>g3$PaQ!FWvK1bxqY*kJ)E~LD$2z_v&vts(aphD}24| z?E!B64iDxrUwDyy2ExyRDDW#?EvpBf?`4gsftnhti)Pk}T8JC0DcV^l>Nwm&+>N>p zw?!}OM}3DohzHRCaTgl#3IwlLSr1yQ$NFr*R{p9vybAqmyarCs*8i!!u`D*&=AT}) z#(rR1_*`fHQ*Zmm6>|0-Z~Kd8lFKPiuWBfx*h;~~AZJ&8C3gyHlCzi=1zz?_`L($A z+xW@hv(eEwetI;1emputla=3}j30f`oS=UYpyrEB#*TCtV}?~8q|y@5KEfDV)G(bFTPW5*HjPbsewIK@JMifvjUVxxV61Cp7GCvKYXl( z%{`zuF}Y)jbjl2*AtT3-OsEEei3*-j!*fZfWW<~SIX$8J#TZhX!2Fcxyi0JIGiuOe zP9$6qi7vU%&NHLJ77hcYY_pHTzhjH+;&3kFJum-oW`iM%-9-8;Xp?mdvj zZu~Np`}>ypu>owH|p8v1+mwSg?V*m*Wc25YRc`C8MuGtE*dO1B;&mHk)v-2>Sm#p{)nm$-E19G8pZwcD kXZR5c_{Dzy`ZgZzpzm+sUjAy$dfg8y`D6W4z1!}520gW3?f?J) literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__pycache__/signals.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__pycache__/signals.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c7fa6752dc251a9799f021415f5dd99c321ac6c6 GIT binary patch literal 4330 zcmbtX&2!tv6$c2A1SpE4J}vnZZIiZ4YRPd@x1Mp_Y8*9b?Z_I5ZfYEgLfj<^j~z*o_^n;k?4lwT7Nrf_H*giUY{TRuJM4!3YMS8=4DJlFG_l7d)#jlL4ZDoCP{n69ruOIAqJL{isJ=*ZL*P9#O!;Pny zFuSw0zUh6wvA*+Ud*hS5(cX#tuz3*i7$YhjF1_BM)8PWO+Gow?)|MDVe#e&sYMJt- z^jmvgj5$smL~En&A(pq&(sin83b60>W|N_Q$LvhqtHl4NGBXh$XL z=~m@*x0aMJZ${R;v&?>J9+hd#XzoNivJwmX*y3MFc{DE<5=)(zR1xg^l6#DtE7%?4R;TAkOUi$QFln7qOX(pks>W1rx%Dp|Z$I4F_O>5C zZhGW7sYxdlRa&`}b*3w{y$`D#$ef6Q`wT z&(+%`or*F(Lq{O1HBB1bj0|FC^Vy@SM;i0M`;>Mx&_%R)bs_kUoKRrj#Avh$a5ZE zL<6Z^dH7clzN?3m7abSQy!sFDYQ%~OuQo?TS?ZSYC=Zl&VkY`AxI;;WF8q~hi8-pC zXlR|zRs`RJ6B?yWv4B%5D;=&x1AuAlkGX5X6U7n+9li^v;V%HVAnZ6~!rXn@x!-^X zF}6_?h>4oGO2suQW>KVNfFi_1@gr(_hnlL*^__=abNy3dY}Zn??I)XJo<=w{!twy{ zmh`-|)DFTR-V=2+H;UrNG^9brdsGm}#80RoFlEIWq;xvBZg#Psp((zO;=Fm4Ibmip zb`7Q#KgEc1W+jlH5%($r*VwF5?-oWNS4G_hR8JJF=sI4_sra6Rkr+l~N|&Wk83xp& ztxSU9^rIq3m?-}DWEN?6E6h+tT7)?{C7}rUCy3Sa9z&PUaEDv zI}aLP$jP}5=$ItS#iX3WWC=8FCS^)HULqAydm{*Zsf+gl?Z}pwM$1XLyOP*y4GLKH ziT`%qls|2uAK=BnE8Qx=54-#SP*z zh8)_NpRNY1`LzzxjlF2N+k^ex`ANbf;}WDUVNT;}20O|jKz5*>Sj<75z#;LfAoGhG zRFI#F?^E$TDtyankinN2 zF|FXHd5+HsnxXwB zogl~fX%woed3o)kKP+Vau*>1I{8XumtUkh0T9G)HU3vIS)FLo;@r&-)&yih^GE%G3250G>0iNW(22!12^@A)w@Y>~p1RjdwMJhl0Y z7LO#-HSr7F)~!-rfi_PqXi|2R7UXVY(iZ28y|9yHz_Gd4J0J51K=(cAwH$zP0pBqKB@!%6N6$3DHHYwotc!@fyM|2 z;y38sY&cmcpzz_A^Yl?=;#nH438FO>A5;oHPJUB^zD-o}JcF+TE|4^@W}lj3npUfy zMgbswbx+Dj)X39J{mJD*ty-{LNkpu9|@9bY{takga|xjm?VZzh=G_1CoK%((s`V)l&R^Ss3K#+xT4@r0=OG*2l+$Fu#o zHx%b*-Gj3yYX7N_-ILEF(d*5gb*+qKd=yJAJGKWf3Gk+)zQWe lTk*t(II_6byMPTGhv>kzUSv5fQufLYMBD7!sO`6Qe*>iXGWh@i literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__pycache__/utils.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/__pycache__/utils.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e329726c85b761c2eefded24de7ea7e0fd1966a3 GIT binary patch literal 3801 zcmaJ^TW=f372cafin>_wB^M`6CQf3DtxU>EVZg3xB#@j22%rS8Dknr!aI#OME%1 z463cFZHX0L;Z?rEYkZZj@pZnzFY!(O8o$i1@T>e9f1SU<-|St!uv<0$7QgBbB|wZ`xlz{I>4ot3fhIrBDhx$Cm>U2{~#k%+;ON zMvzD;BAc)U5=Q_f&{or@?u1^spyca!nea?8(VA(1Pz zKdzOF=;LHnoCSS9?uqfG>3lhX{S|SRhR|TVF1fKTXd~C$Q*1m6h^H z$Z2;f!$gL9G~+Uzz~a)=Ok`rbRvs`Tl1z#;8ntyYJ2RcYnTixzh6|FFW1L%+Nv6ZN z*FF)Wnb{ZSaJr_14E-n^3pswRWILb2h7}zSV9`OSXXc*GV0jt3^uk!KkF|yAyZfy+9g01ucYfb^OT}B1(m1I$`2W zOFz(T0N1lqietsPG8r(R?V0Fgdr7RZ>maw}Wr!J2P){FH$i=JZeI3oj`flUgp4i+v zsUBHi>2M1y?St>hC{AN1x7(67n&zjh37?jYb2}#PH;WBMvP>b&aQqeE*#RYAGo=%N4EF5%+UH4+5ht8O5*t#)uCt}V z2^hJ}Le);Q<0w@9dWZRR4rVSmV~zNAvxp7*5RcGM4h+1qL8df27KY+XFJ2@-5{qdH zsIV9N5v7YCC*Uj_HFISD;n98eSP*&kN?B%>fix!DVwFizXjsY%g_v)sk%bs(RPY~$ zx*x_Q5`U=xVI0-t#L@rWU=x)H7(RmxmXdI1L-%nE!t3r7JqDXwdHh9@suslsH{5V6nkId;%aJD5Hy$0)$77asjtKO+^ zv!P%DT(Xpb$<&2~z6mQ0F%^pRHqoSzA1s7y$_$NVf((~nOlrw2jzMWr>#wCa4U^op(*&HvXWFc-5z97QhPk7u|vKO@gEG25vLlja}#W)?+Ka>RH-3smc2`E*snW z){32PO4u6zWsX@=4B&GZtpqH}t7&qZxGIoA-%@N20}W#)lS(t-LRrKk zdwA6j8WcGY9_5m4dQ><;EqMcFl*L_AZQ@LO+{2x=H2(K1lr$M`bOG!DWx{Sqxl7d$ zEe%k%`#o6TIN_ttLUi=$$8;Zw`D&Tv##H3x zFam&-2uJi51l-KmalOL#*WM-8N1_xW0#>Y*5pr<|T)1&KyQ*0BCrM zO+|_?*X)|@IW26}htlUyN~=;i`I@s8G>ku*9T*2^7P3L0}`gsi~^FgM??1ctB;F+C{Tw literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/app.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/app.py new file mode 100644 index 000000000..3a11b8e76 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/app.py @@ -0,0 +1,59 @@ +from celery import signals + +from ddtrace import Pin +from ddtrace import config +from ddtrace.pin import _DD_PIN_NAME + +from .constants import APP +from .signals import trace_after_publish +from .signals import trace_before_publish +from .signals import trace_failure +from .signals import trace_postrun +from .signals import trace_prerun +from .signals import trace_retry + + +def patch_app(app, pin=None): + """Attach the Pin class to the application and connect + our handlers to Celery signals. + """ + if getattr(app, "__datadog_patch", False): + return + setattr(app, "__datadog_patch", True) + + # attach the PIN object + pin = pin or Pin( + service=config.celery["worker_service_name"], + app=APP, + _config=config.celery, + ) + pin.onto(app) + # connect to the Signal framework + + signals.task_prerun.connect(trace_prerun, weak=False) + signals.task_postrun.connect(trace_postrun, weak=False) + signals.before_task_publish.connect(trace_before_publish, weak=False) + signals.after_task_publish.connect(trace_after_publish, weak=False) + signals.task_failure.connect(trace_failure, weak=False) + signals.task_retry.connect(trace_retry, weak=False) + return app + + +def unpatch_app(app): + """Remove the Pin instance from the application and disconnect + our handlers from Celery signal framework. + """ + if not getattr(app, "__datadog_patch", False): + return + setattr(app, "__datadog_patch", False) + + pin = Pin.get_from(app) + if pin is not None: + delattr(app, _DD_PIN_NAME) + + signals.task_prerun.disconnect(trace_prerun) + signals.task_postrun.disconnect(trace_postrun) + signals.before_task_publish.disconnect(trace_before_publish) + signals.after_task_publish.disconnect(trace_after_publish) + signals.task_failure.disconnect(trace_failure) + signals.task_retry.disconnect(trace_retry) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/constants.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/constants.py new file mode 100644 index 000000000..496f9ce2e --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/constants.py @@ -0,0 +1,21 @@ +from ddtrace import config + + +# Celery Context key +CTX_KEY = "__dd_task_span" + +# Span names +PRODUCER_ROOT_SPAN = "celery.apply" +WORKER_ROOT_SPAN = "celery.run" + +# Task operations +TASK_TAG_KEY = "celery.action" +TASK_APPLY = "apply" +TASK_APPLY_ASYNC = "apply_async" +TASK_RUN = "run" +TASK_RETRY_REASON_KEY = "celery.retry.reason" + +# Service info +APP = "celery" +PRODUCER_SERVICE = config._get_service(default="celery-producer") +WORKER_SERVICE = config._get_service(default="celery-worker") diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/patch.py new file mode 100644 index 000000000..7a4402918 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/patch.py @@ -0,0 +1,38 @@ +import celery + +from ddtrace import config +import ddtrace.internal.forksafe as forksafe + +from ...utils.formats import get_env +from .app import patch_app +from .app import unpatch_app +from .constants import PRODUCER_SERVICE +from .constants import WORKER_SERVICE + + +forksafe._soft = True + + +# Celery default settings +config._add( + "celery", + { + "distributed_tracing": get_env("celery", "distributed_tracing", default=False), + "producer_service_name": get_env("celery", "producer_service_name", default=PRODUCER_SERVICE), + "worker_service_name": get_env("celery", "worker_service_name", default=WORKER_SERVICE), + }, +) + + +def patch(): + """Instrument Celery base application and the `TaskRegistry` so + that any new registered task is automatically instrumented. In the + case of Django-Celery integration, also the `@shared_task` decorator + must be instrumented because Django doesn't use the Celery registry. + """ + patch_app(celery.Celery) + + +def unpatch(): + """Disconnect all signals and remove Tracing capabilities""" + unpatch_app(celery.Celery) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/signals.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/signals.py new file mode 100644 index 000000000..3b6be094f --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/signals.py @@ -0,0 +1,193 @@ +from celery import registry + +from ddtrace import Pin +from ddtrace import config + +from . import constants as c +from .. import trace_utils +from ...constants import ANALYTICS_SAMPLE_RATE_KEY +from ...constants import SPAN_MEASURED_KEY +from ...ext import SpanTypes +from ...internal.logger import get_logger +from ...propagation.http import HTTPPropagator +from .utils import attach_span +from .utils import detach_span +from .utils import retrieve_span +from .utils import retrieve_task_id +from .utils import set_tags_from_context + + +log = get_logger(__name__) +propagator = HTTPPropagator + + +def trace_prerun(*args, **kwargs): + # safe-guard to avoid crashes in case the signals API + # changes in Celery + task = kwargs.get("sender") + task_id = kwargs.get("task_id") + log.debug("prerun signal start task_id=%s", task_id) + if task is None or task_id is None: + log.debug("unable to extract the Task and the task_id. This version of Celery may not be supported.") + return + + # retrieve the task Pin or fallback to the global one + pin = Pin.get_from(task) or Pin.get_from(task.app) + if pin is None: + log.debug("no pin found on task or task.app task_id=%s", task_id) + return + + request_headers = task.request.get("headers", {}) + trace_utils.activate_distributed_headers(pin.tracer, int_config=config.celery, request_headers=request_headers) + + # propagate the `Span` in the current task Context + service = config.celery["worker_service_name"] + span = pin.tracer.trace(c.WORKER_ROOT_SPAN, service=service, resource=task.name, span_type=SpanTypes.WORKER) + # set analytics sample rate + rate = config.celery.get_analytics_sample_rate() + if rate is not None: + span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, rate) + + span.set_tag(SPAN_MEASURED_KEY) + attach_span(task, task_id, span) + + +def trace_postrun(*args, **kwargs): + # safe-guard to avoid crashes in case the signals API + # changes in Celery + task = kwargs.get("sender") + task_id = kwargs.get("task_id") + log.debug("postrun signal task_id=%s", task_id) + if task is None or task_id is None: + log.debug("unable to extract the Task and the task_id. This version of Celery may not be supported.") + return + + # retrieve and finish the Span + span = retrieve_span(task, task_id) + if span is None: + log.warning("no existing span found for task_id=%s", task_id) + return + else: + # request context tags + span.set_tag(c.TASK_TAG_KEY, c.TASK_RUN) + set_tags_from_context(span, kwargs) + set_tags_from_context(span, task.request.__dict__) + span.finish() + detach_span(task, task_id) + + +def trace_before_publish(*args, **kwargs): + # `before_task_publish` signal doesn't propagate the task instance so + # we need to retrieve it from the Celery Registry to access the `Pin`. The + # `Task` instance **does not** include any information about the current + # execution, so it **must not** be used to retrieve `request` data. + task_name = kwargs.get("sender") + task = registry.tasks.get(task_name) + task_id = retrieve_task_id(kwargs) + # safe-guard to avoid crashes in case the signals API + # changes in Celery + if task is None or task_id is None: + log.debug("unable to extract the Task and the task_id. This version of Celery may not be supported.") + return + + # propagate the `Span` in the current task Context + pin = Pin.get_from(task) or Pin.get_from(task.app) + if pin is None: + return + + # apply some tags here because most of the data is not available + # in the task_after_publish signal + service = config.celery["producer_service_name"] + span = pin.tracer.trace(c.PRODUCER_ROOT_SPAN, service=service, resource=task_name) + # set analytics sample rate + rate = config.celery.get_analytics_sample_rate() + if rate is not None: + span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, rate) + + span.set_tag(SPAN_MEASURED_KEY) + span.set_tag(c.TASK_TAG_KEY, c.TASK_APPLY_ASYNC) + span.set_tag("celery.id", task_id) + set_tags_from_context(span, kwargs) + + # Note: adding tags from `traceback` or `state` calls will make an + # API call to the backend for the properties so we should rely + # only on the given `Context` + attach_span(task, task_id, span, is_publish=True) + + if config.celery["distributed_tracing"]: + trace_headers = {} + propagator.inject(span.context, trace_headers) + + # This weirdness is due to yet another Celery bug concerning + # how headers get propagated in async flows + # https://github.com/celery/celery/issues/4875 + task_headers = kwargs.get("headers") or {} + task_headers.setdefault("headers", {}) + task_headers["headers"].update(trace_headers) + kwargs["headers"] = task_headers + + +def trace_after_publish(*args, **kwargs): + task_name = kwargs.get("sender") + task = registry.tasks.get(task_name) + task_id = retrieve_task_id(kwargs) + # safe-guard to avoid crashes in case the signals API + # changes in Celery + if task is None or task_id is None: + log.debug("unable to extract the Task and the task_id. This version of Celery may not be supported.") + return + + # retrieve and finish the Span + span = retrieve_span(task, task_id, is_publish=True) + if span is None: + return + else: + span.finish() + detach_span(task, task_id, is_publish=True) + + +def trace_failure(*args, **kwargs): + # safe-guard to avoid crashes in case the signals API + # changes in Celery + task = kwargs.get("sender") + task_id = kwargs.get("task_id") + if task is None or task_id is None: + log.debug("unable to extract the Task and the task_id. This version of Celery may not be supported.") + return + + # retrieve and finish the Span + span = retrieve_span(task, task_id) + if span is None: + return + else: + # add Exception tags; post signals are still called + # so we don't need to attach other tags here + ex = kwargs.get("einfo") + if ex is None: + return + if hasattr(task, "throws") and isinstance(ex.exception, task.throws): + return + span.set_exc_info(ex.type, ex.exception, ex.tb) + + +def trace_retry(*args, **kwargs): + # safe-guard to avoid crashes in case the signals API + # changes in Celery + task = kwargs.get("sender") + context = kwargs.get("request") + if task is None or context is None: + log.debug("unable to extract the Task or the Context. This version of Celery may not be supported.") + return + + reason = kwargs.get("reason") + if not reason: + log.debug("unable to extract the retry reason. This version of Celery may not be supported.") + return + + span = retrieve_span(task, context.id) + if span is None: + return + + # Add retry reason metadata to span + # DEV: Use `str(reason)` instead of `reason.message` in case we get something that isn't an `Exception` + span.set_tag(c.TASK_RETRY_REASON_KEY, str(reason)) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/task.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/task.py new file mode 100644 index 000000000..b73a63d5c --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/task.py @@ -0,0 +1,31 @@ +from ...utils.deprecation import deprecation +from .app import patch_app + + +def patch_task(task, pin=None): + """Deprecated API. The new API uses signals that can be activated via + patch(celery=True) or through `ddtrace-run` script. Using this API + enables instrumentation on all tasks. + """ + deprecation( + name="ddtrace.contrib.celery.patch_task", + message="Use `patch(celery=True)` or `ddtrace-run` script instead", + version="1.0.0", + ) + + # Enable instrumentation everywhere + patch_app(task.app) + return task + + +def unpatch_task(task): + """Deprecated API. The new API uses signals that can be deactivated + via unpatch() API. This API is now a no-op implementation so it doesn't + affect instrumented tasks. + """ + deprecation( + name="ddtrace.contrib.celery.patch_task", + message="Use `unpatch()` instead", + version="1.0.0", + ) + return task diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/utils.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/utils.py new file mode 100644 index 000000000..b8c245198 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/celery/utils.py @@ -0,0 +1,126 @@ +from typing import Any +from typing import Dict +from weakref import WeakValueDictionary + +from ddtrace.span import Span + +from .constants import CTX_KEY + + +TAG_KEYS = frozenset( + [ + ("compression", "celery.compression"), + ("correlation_id", "celery.correlation_id"), + ("countdown", "celery.countdown"), + ("delivery_info", "celery.delivery_info"), + ("eta", "celery.eta"), + ("exchange", "celery.exchange"), + ("expires", "celery.expires"), + ("hostname", "celery.hostname"), + ("id", "celery.id"), + ("priority", "celery.priority"), + ("queue", "celery.queue"), + ("reply_to", "celery.reply_to"), + ("retries", "celery.retries"), + ("routing_key", "celery.routing_key"), + ("serializer", "celery.serializer"), + ("timelimit", "celery.timelimit"), + # Celery 4.0 uses `origin` instead of `hostname`; this change preserves + # the same name for the tag despite Celery version + ("origin", "celery.hostname"), + ("state", "celery.state"), + ] +) + + +def set_tags_from_context(span, context): + # type: (Span, Dict[str, Any]) -> None + """Helper to extract meta values from a Celery Context""" + + for key, tag_name in TAG_KEYS: + value = context.get(key) + + # Skip this key if it is not set + if value is None or value == "": + continue + + # Skip `timelimit` if it is not set (its default/unset value is a + # tuple or a list of `None` values + if key == "timelimit" and all(_ is None for _ in value): + continue + + # Skip `retries` if its value is `0` + if key == "retries" and value == 0: + continue + + span.set_tag(tag_name, value) + + +def attach_span(task, task_id, span, is_publish=False): + """Helper to propagate a `Span` for the given `Task` instance. This + function uses a `WeakValueDictionary` that stores a Datadog Span using + the `(task_id, is_publish)` as a key. This is useful when information must be + propagated from one Celery signal to another. + + DEV: We use (task_id, is_publish) for the key to ensure that publishing a + task from within another task does not cause any conflicts. + + This mostly happens when either a task fails and a retry policy is in place, + or when a task is manually retried (e.g. `task.retry()`), we end up trying + to publish a task with the same id as the task currently running. + + Previously publishing the new task would overwrite the existing `celery.run` span + in the `weak_dict` causing that span to be forgotten and never finished. + + NOTE: We cannot test for this well yet, because we do not run a celery worker, + and cannot run `task.apply_async()` + """ + weak_dict = getattr(task, CTX_KEY, None) + if weak_dict is None: + weak_dict = WeakValueDictionary() + setattr(task, CTX_KEY, weak_dict) + + weak_dict[(task_id, is_publish)] = span + + +def detach_span(task, task_id, is_publish=False): + """Helper to remove a `Span` in a Celery task when it's propagated. + This function handles tasks where the `Span` is not attached. + """ + weak_dict = getattr(task, CTX_KEY, None) + if weak_dict is None: + return + + # DEV: See note in `attach_span` for key info + try: + del weak_dict[(task_id, is_publish)] + except KeyError: + pass + + +def retrieve_span(task, task_id, is_publish=False): + """Helper to retrieve an active `Span` stored in a `Task` + instance + """ + weak_dict = getattr(task, CTX_KEY, None) + if weak_dict is None: + return + else: + # DEV: See note in `attach_span` for key info + return weak_dict.get((task_id, is_publish)) + + +def retrieve_task_id(context): + """Helper to retrieve the `Task` identifier from the message `body`. + This helper supports Protocol Version 1 and 2. The Protocol is well + detailed in the official documentation: + http://docs.celeryproject.org/en/latest/internals/protocol.html + """ + headers = context.get("headers") + body = context.get("body") + if headers: + # Protocol Version 2 (default from Celery 4.0) + return headers.get("id") + else: + # Protocol Version 1 + return body.get("id") diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cherrypy/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cherrypy/__init__.py new file mode 100644 index 000000000..6dc304aa0 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cherrypy/__init__.py @@ -0,0 +1,65 @@ +""" +The Cherrypy trace middleware will track request timings. +It uses the cherrypy hooks and creates a tool to track requests and errors + + +Usage +~~~~~ +To install the middleware, add:: + + from ddtrace import tracer + from ddtrace.contrib.cherrypy import TraceMiddleware + +and create a `TraceMiddleware` object:: + + traced_app = TraceMiddleware(cherrypy, tracer, service="my-cherrypy-app") + + +Configuration +~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.cherrypy['distributed_tracing'] + + Whether to parse distributed tracing headers from requests received by your CherryPy app. + + Can also be enabled with the ``DD_CHERRYPY_DISTRIBUTED_TRACING`` environment variable. + + Default: ``True`` + +.. py:data:: ddtrace.config.cherrypy['service'] + + The service name reported for your CherryPy app. + + Can also be configured via the ``DD_SERVICE`` environment variable. + + Default: ``'cherrypy'`` + + +Example:: +Here is the end result, in a sample app:: + + import cherrypy + + from ddtrace import tracer, Pin + from ddtrace.contrib.cherrypy import TraceMiddleware + TraceMiddleware(cherrypy, tracer, service="my-cherrypy-app") + + @cherrypy.tools.tracer() + class HelloWorld(object): + def index(self): + return "Hello World" + index.exposed = True + + cherrypy.quickstart(HelloWorld()) +""" + +from ..util import require_modules + + +required_modules = ["cherrypy"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .middleware import TraceMiddleware + + __all__ = ["TraceMiddleware"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cherrypy/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cherrypy/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..80d74ca512f32c92e6a8415770d720c419859704 GIT binary patch literal 1745 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 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cherrypy/middleware.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cherrypy/middleware.py new file mode 100644 index 000000000..9237d8fa3 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/cherrypy/middleware.py @@ -0,0 +1,146 @@ +""" +Datadog trace code for cherrypy. +""" + +# stdlib +import logging + +# 3p +import cherrypy +from cherrypy.lib.httputil import valid_status + +# project +from ddtrace import config +from ddtrace.constants import ERROR_MSG +from ddtrace.constants import ERROR_STACK +from ddtrace.constants import ERROR_TYPE + +from .. import trace_utils +from ...ext import SpanTypes +from ...internal import compat +from ...utils.formats import asbool +from ...utils.formats import get_env + + +log = logging.getLogger(__name__) + + +# Configure default configuration +config._add( + "cherrypy", + dict( + distributed_tracing=asbool(get_env("cherrypy", "distributed_tracing", default=True)), + ), +) + +SPAN_NAME = "cherrypy.request" + + +class TraceTool(cherrypy.Tool): + def __init__(self, app, tracer, service, use_distributed_tracing=None): + self.app = app + self._tracer = tracer + self.service = service + if use_distributed_tracing is not None: + self.use_distributed_tracing = use_distributed_tracing + + # CherryPy uses priority to determine which tools act first on each event. The lower the number, the higher + # the priority. See: https://docs.cherrypy.org/en/latest/extend.html#tools-ordering + cherrypy.Tool.__init__(self, "on_start_resource", self._on_start_resource, priority=95) + + @property + def use_distributed_tracing(self): + return config.cherrypy.distributed_tracing + + @use_distributed_tracing.setter + def use_distributed_tracing(self, use_distributed_tracing): + config.cherrypy["distributed_tracing"] = asbool(use_distributed_tracing) + + @property + def service(self): + return config.cherrypy.get("service", "cherrypy") + + @service.setter + def service(self, service): + config.cherrypy["service"] = service + + def _setup(self): + cherrypy.Tool._setup(self) + cherrypy.request.hooks.attach("on_end_request", self._on_end_request, priority=5) + cherrypy.request.hooks.attach("after_error_response", self._after_error_response, priority=5) + + def _on_start_resource(self): + trace_utils.activate_distributed_headers( + self._tracer, int_config=config.cherrypy, request_headers=cherrypy.request.headers + ) + + cherrypy.request._datadog_span = self._tracer.trace( + SPAN_NAME, + service=trace_utils.int_service(None, config.cherrypy, default="cherrypy"), + span_type=SpanTypes.WEB, + ) + + def _after_error_response(self): + span = getattr(cherrypy.request, "_datadog_span", None) + + if not span: + log.warning("cherrypy: tracing tool after_error_response hook called, but no active span found") + return + + span.error = 1 + span.set_tag(ERROR_TYPE, cherrypy._cperror._exc_info()[0]) + span.set_tag(ERROR_MSG, str(cherrypy._cperror._exc_info()[1])) + span.set_tag(ERROR_STACK, cherrypy._cperror.format_exc()) + + self._close_span(span) + + def _on_end_request(self): + span = getattr(cherrypy.request, "_datadog_span", None) + + if not span: + log.warning("cherrypy: tracing tool on_end_request hook called, but no active span found") + return + + self._close_span(span) + + def _close_span(self, span): + # Let users specify their own resource in middleware if they so desire. + # See case https://github.com/DataDog/dd-trace-py/issues/353 + if span.resource == SPAN_NAME: + # In the future, mask virtual path components in a + # URL e.g. /dispatch/abc123 becomes /dispatch/{{test_value}}/ + # Following investigation, this should be possible using + # [find_handler](https://docs.cherrypy.org/en/latest/_modules/cherrypy/_cpdispatch.html#Dispatcher.find_handler) + # but this may not be as easy as `cherrypy.request.dispatch.find_handler(cherrypy.request.path_info)` as + # this function only ever seems to return an empty list for the virtual path components. + + # For now, default resource is method and path: + # GET / + # POST /save + resource = "{} {}".format(cherrypy.request.method, cherrypy.request.path_info) + span.resource = compat.to_unicode(resource) + + url = compat.to_unicode(cherrypy.request.base + cherrypy.request.path_info) + status_code, _, _ = valid_status(cherrypy.response.status) + + trace_utils.set_http_meta( + span, + config.cherrypy, + method=cherrypy.request.method, + url=url, + status_code=status_code, + request_headers=cherrypy.request.headers, + response_headers=cherrypy.response.headers, + ) + + span.finish() + + # Clear our span just in case. + cherrypy.request._datadog_span = None + + +class TraceMiddleware(object): + def __init__(self, app, tracer, service="cherrypy", distributed_tracing=None): + self.app = app + + self.app.tools.tracer = TraceTool(app, tracer, service, distributed_tracing) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/consul/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/consul/__init__.py new file mode 100644 index 000000000..253c43503 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/consul/__init__.py @@ -0,0 +1,32 @@ +"""Instrument Consul to trace KV queries. + +Only supports tracing for the synchronous client. + +``patch_all`` will automatically patch your Consul client to make it work. +:: + + from ddtrace import Pin, patch + import consul + + # If not patched yet, you can patch consul specifically + patch(consul=True) + + # This will report a span with the default settings + client = consul.Consul(host="127.0.0.1", port=8500) + client.get("my-key") + + # Use a pin to specify metadata related to this client + Pin.override(client, service='consul-kv') +""" + +from ...utils.importlib import require_modules + + +required_modules = ["consul"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + from .patch import unpatch + + __all__ = ["patch", "unpatch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/consul/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/consul/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6c3137ec73c8be37dc43ae1e7924d5bd2714f77d GIT binary patch literal 1008 zcmYjQ%W~5&6qTK4JuPL!UK18gn8r>DEu=#jhO+4}!=tpoc;l*;lBlsISCW&uTRwo{ z2iWsV-LmR0u;SWEORvYWbRS3OT*)UJ8y*7N`}LE&sUh^IT->)cSiFW;AHhITh^Rx| z#7W)Ibr7vhov_ASR{QS!coNoGUB9Ju+DIB{BWystPMa`qhE15aXnX30t*>YoeL_d* z8=`BUQO_e=E<%Sw85=?RjHTS*!eb2*S6D3njspZGhk_`UH#kPg2QdZu$5@V|#ij7)-A-Ec=d9@5F*(r; z6P$5THqI(2aLNp!#1O1lLJWv5&$Xm%8C0OqI$tiBQk=4`^=|`Rwcs%uZ&`-@`C_Z* z-M|e+J?DB;v5TB57NwHr3DaTIX5Bb8-l+NoiLG2zQ&oq6YQXTm*S>B=5tVTig`2tI ziT3SbCHzdmRYc*#6*|3x-%PpI(7ap6f4CA0Zc!BdMyg%zQ4W7EgG})S=nWP`1rwQ2 zrgU&M3`S>Sc|O=X-%-P-A{#`PQ!yG{JsXrZ&4S|~<5{1>&j6VH#Z@*cij)Pbt9JJV ru}1N;z={r{hzo9_$j^%F_DkE6S7j&k0~qR$)@x1IbL!5zv+n)_ZJ06( literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/consul/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/consul/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6e7d33db7e97e8ba520406bcb64d5d0a0a99ba85 GIT binary patch literal 1976 zcmZ8h&2QT_6emSlk`>!YoR79!x3-62^PzUTk75CeVutHtMOq_^-E1W~2&$>LR%KZw z>7)tdQwnUCZrEjq9fkxu_21c%*PZ$=?6gPPL6TA6ITHEJg7VJ*az8sFzKyz8d-|HvA>6K=u`RHlyVXGT-n~%31 zwS(q-A(= zL@<&>+HnDhS44bo*pDayM{y6#I4rfsDZH}2xl{i{)XK_*7lcxBbE+@Q zxFX0Z7aR|=uc_N@Z|^*8wJB+CZBcui%CT~0ij=jvql&@iPVnGKr`1(fu&>G&!UT4Z zY?vx%H%P-#^bYY4LHWOvM#hJGp^O@PA#d!C6BcpNIK181c%JUZx9-HB^V?sf*{$I9 zAl=wF{PLC_gRIeOWW(&{FcmUPlAC*n*~Y=aC~7b!dDuT&!F0)o&l|HRG>lK`*?}q; zR0Rt}1qi|=Y~zx(ge#VZ`A4vGA&wX}Y?vmFUjsOcqc|T&h^vkxM5;PP(uU|2VkbvO zP*1O}n;4M}UxfP)YA#=f)%crhJNP;f@Y{s1YHkfk4f78)){dHytALa{NsaX%10zh8 z&#ec`ufbklTYEZm29o&{O8|aCyCwtQcvGC~3L~Aqe%k5KA=gS*V2ZziNOTN>CwOY- zHeb)}W9tNo>p9A;ocsa`B*}5C6W58|*gLYs-E(I}EoBnko)&&a$NmYLI=LM$#3fmd zD}WYrM=r(7jJ&mfeKvJ-_qZxoOv10m&hZ+^d%4HZ%Od~XoUP~H3AoeXa(pQ-oZ~9F zW4O6(-y;isg}y`E=r1S_z5W1g4W4Xwxb~4EFQbDRRHDWnkHmPwp-PGZ+Cd05BkD$J_&kZ2@`u7CUkVor;Q(5LvR_C!D=$ifWYqt**V}3(LpbBwqpC2` zeTq6JMY$@>R_$m?T95A=J2*6Jq-=ea>M{&uIN-X4a2-i{#WbTdOb+C*F9H#cvLp&P z)DJHhJ-W>ml{@Q+OnIk9DjQmuP|nMJz49yOFOWtr_zeT~o2ji8y~E1Z6?!h?d}FEU zxSxTv(3yv@4{7I= 0: + s.set_tag(sql.ROWS, row_count) + + def executemany(self, query, *args, **kwargs): + """Wraps the cursor.executemany method""" + self._self_last_execute_operation = query + # Always return the result as-is + # DEV: Some libraries return `None`, others `int`, and others the cursor objects + # These differences should be overridden at the integration specific layer (e.g. in `sqlite3/patch.py`) + # FIXME[matt] properly handle kwargs here. arg names can be different + # with different libs. + return self._trace_method( + self.__wrapped__.executemany, + self._self_datadog_name, + query, + {"sql.executemany": "true"}, + query, + *args, + **kwargs + ) + + def execute(self, query, *args, **kwargs): + """Wraps the cursor.execute method""" + self._self_last_execute_operation = query + + # Always return the result as-is + # DEV: Some libraries return `None`, others `int`, and others the cursor objects + # These differences should be overridden at the integration specific layer (e.g. in `sqlite3/patch.py`) + return self._trace_method(self.__wrapped__.execute, self._self_datadog_name, query, {}, query, *args, **kwargs) + + def callproc(self, proc, *args): + """Wraps the cursor.callproc method""" + self._self_last_execute_operation = proc + return self._trace_method(self.__wrapped__.callproc, self._self_datadog_name, proc, {}, proc, *args) + + def __enter__(self): + # previous versions of the dbapi didn't support context managers. let's + # reference the func that would be called to ensure that errors + # messages will be the same. + self.__wrapped__.__enter__ + + # and finally, yield the traced cursor. + return self + + +class FetchTracedCursor(TracedCursor): + """ + Sub-class of :class:`TracedCursor` that also instruments `fetchone`, `fetchall`, and `fetchmany` methods. + + We do not trace these functions by default since they can get very noisy (e.g. `fetchone` with 100k rows). + """ + + def fetchone(self, *args, **kwargs): + """Wraps the cursor.fetchone method""" + span_name = "{}.{}".format(self._self_datadog_name, "fetchone") + return self._trace_method( + self.__wrapped__.fetchone, span_name, self._self_last_execute_operation, {}, *args, **kwargs + ) + + def fetchall(self, *args, **kwargs): + """Wraps the cursor.fetchall method""" + span_name = "{}.{}".format(self._self_datadog_name, "fetchall") + return self._trace_method( + self.__wrapped__.fetchall, span_name, self._self_last_execute_operation, {}, *args, **kwargs + ) + + def fetchmany(self, *args, **kwargs): + """Wraps the cursor.fetchmany method""" + span_name = "{}.{}".format(self._self_datadog_name, "fetchmany") + # We want to trace the information about how many rows were requested. Note that this number may be larger + # the number of rows actually returned if less then requested are available from the query. + size_tag_key = "db.fetch.size" + + try: + extra_tags = {size_tag_key: get_argument_value(args, kwargs, 0, "size")} + except ArgumentError: + default_array_size = getattr(self.__wrapped__, "arraysize", None) + extra_tags = {size_tag_key: default_array_size} if default_array_size else {} + + return self._trace_method( + self.__wrapped__.fetchmany, span_name, self._self_last_execute_operation, extra_tags, *args, **kwargs + ) + + +class TracedConnection(wrapt.ObjectProxy): + """TracedConnection wraps a Connection with tracing code.""" + + def __init__(self, conn, pin=None, cfg=None, cursor_cls=None): + if not cfg: + cfg = config.dbapi2 + # Set default cursor class if one was not provided + if not cursor_cls: + # Do not trace `fetch*` methods by default + cursor_cls = FetchTracedCursor if cfg.trace_fetch_methods else TracedCursor + + super(TracedConnection, self).__init__(conn) + name = _get_vendor(conn) + self._self_datadog_name = "{}.connection".format(name) + db_pin = pin or Pin(service=name, app=name) + db_pin.onto(self) + # wrapt requires prefix of `_self` for attributes that are only in the + # proxy (since some of our source objects will use `__slots__`) + self._self_cursor_cls = cursor_cls + self._self_config = cfg + + def __enter__(self): + """Context management is not defined by the dbapi spec. + + This means unfortunately that the database clients each define their own + implementations. + + The ones we know about are: + + - mysqlclient<2.0 which returns a cursor instance. >=2.0 returns a + connection instance. + - psycopg returns a connection. + - pyodbc returns a connection. + - pymysql doesn't implement it. + - sqlite3 returns the connection. + """ + r = self.__wrapped__.__enter__() + + if hasattr(r, "cursor"): + # r is Connection-like. + if r is self.__wrapped__: + # Return the reference to this proxy object. Returning r would + # return the untraced reference. + return self + else: + # r is a different connection object. + # This should not happen in practice but play it safe so that + # the original functionality is maintained. + return r + elif hasattr(r, "execute"): + # r is Cursor-like. + if iswrapped(r): + return r + else: + pin = Pin.get_from(self) + if not pin: + return r + return self._self_cursor_cls(r, pin, self._self_config) + else: + # Otherwise r is some other object, so maintain the functionality + # of the original. + return r + + def _trace_method(self, method, name, extra_tags, *args, **kwargs): + pin = Pin.get_from(self) + if not pin or not pin.enabled(): + return method(*args, **kwargs) + + with pin.tracer.trace(name, service=ext_service(pin, self._self_config)) as s: + s.set_tags(pin.tags) + s.set_tags(extra_tags) + + return method(*args, **kwargs) + + def cursor(self, *args, **kwargs): + cursor = self.__wrapped__.cursor(*args, **kwargs) + pin = Pin.get_from(self) + if not pin: + return cursor + return self._self_cursor_cls(cursor, pin, self._self_config) + + def commit(self, *args, **kwargs): + span_name = "{}.{}".format(self._self_datadog_name, "commit") + return self._trace_method(self.__wrapped__.commit, span_name, {}, *args, **kwargs) + + def rollback(self, *args, **kwargs): + span_name = "{}.{}".format(self._self_datadog_name, "rollback") + return self._trace_method(self.__wrapped__.rollback, span_name, {}, *args, **kwargs) + + +def _get_vendor(conn): + """Return the vendor (e.g postgres, mysql) of the given + database. + """ + try: + name = _get_module_name(conn) + except Exception: + log.debug("couldn't parse module name", exc_info=True) + name = "sql" + return sql.normalize_vendor(name) + + +def _get_module_name(conn): + return conn.__class__.__module__.split(".")[0] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dbapi/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dbapi/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e6a5a475aff8a254fb62d5f1dd068df862fcb79a GIT binary patch literal 7915 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 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/__init__.py new file mode 100644 index 000000000..de596bf33 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/__init__.py @@ -0,0 +1,160 @@ +""" +The Django__ integration traces requests, views, template renderers, database +and cache calls in a Django application. + + +Enable Django tracing automatically via ``ddtrace-run``:: + + ddtrace-run python manage.py runserver + + +Django tracing can also be enabled manually:: + + from ddtrace import patch_all + patch_all() + + +To have Django capture the tracer logs, ensure the ``LOGGING`` variable in +``settings.py`` looks similar to:: + + LOGGING = { + 'loggers': { + 'ddtrace': { + 'handlers': ['console'], + 'level': 'WARNING', + }, + }, + } + + +Configuration +~~~~~~~~~~~~~ +.. py:data:: ddtrace.config.django['distributed_tracing_enabled'] + + Whether or not to parse distributed tracing headers from requests received by your Django app. + + Default: ``True`` + +.. py:data:: ddtrace.config.django['service_name'] + + The service name reported for your Django app. + + Can also be configured via the ``DD_SERVICE`` environment variable. + + Default: ``'django'`` + +.. py:data:: ddtrace.config.django['cache_service_name'] + + The service name reported for your Django app cache layer. + + Can also be configured via the ``DD_DJANGO_CACHE_SERVICE_NAME`` environment variable. + + Default: ``'django'`` + +.. py:data:: ddtrace.config.django['database_service_name'] + + A string reported as the service name of the Django app database layer. + + Can also be configured via the ``DD_DJANGO_DATABASE_SERVICE_NAME`` environment variable. + + Takes precedence over database_service_name_prefix. + + Default: ``''`` + +.. py:data:: ddtrace.config.django['database_service_name_prefix'] + + A string to be prepended to the service name reported for your Django app database layer. + + Can also be configured via the ``DD_DJANGO_DATABASE_SERVICE_NAME_PREFIX`` environment variable. + + The database service name is the name of the database appended with 'db'. Has a lower precedence than database_service_name. + + Default: ``''`` + +.. py:data:: ddtrace.config.django["trace_fetch_methods"] + + Whether or not to trace fetch methods. + + Can also configured via the ``DD_DJANGO_TRACE_FETCH_METHODS`` environment variable. + + Default: ``False`` + +.. py:data:: ddtrace.config.django['instrument_middleware'] + + Whether or not to instrument middleware. + + Can also be enabled with the ``DD_DJANGO_INSTRUMENT_MIDDLEWARE`` environment variable. + + Default: ``True`` + +.. py:data:: ddtrace.config.django['instrument_databases'] + + Whether or not to instrument databases. + + Default: ``True`` + +.. py:data:: ddtrace.config.django['instrument_caches'] + + Whether or not to instrument caches. + + Default: ``True`` + +.. py:data:: ddtrace.config.django['trace_query_string'] + + Whether or not to include the query string as a tag. + + Default: ``False`` + +.. py:data:: ddtrace.config.django['include_user_name'] + + Whether or not to include the authenticated user's username as a tag on the root request span. + + Default: ``True`` + +.. py:data:: ddtrace.config.django['use_handler_resource_format'] + + Whether or not to use the resource format `"{method} {handler}"`. Can also be + enabled with the ``DD_DJANGO_USE_HANDLER_RESOURCE_FORMAT`` environment + variable. + + The default resource format for Django >= 2.2.0 is otherwise `"{method} {urlpattern}"`. + + Default: ``False`` + +.. py:data:: ddtrace.config.django['use_legacy_resource_format'] + + Whether or not to use the legacy resource format `"{handler}"`. Can also be + enabled with the ``DD_DJANGO_USE_LEGACY_RESOURCE_FORMAT`` environment + variable. + + The default resource format for Django >= 2.2.0 is otherwise `"{method} {urlpattern}"`. + + Default: ``False`` + +Example:: + + from ddtrace import config + + # Enable distributed tracing + config.django['distributed_tracing_enabled'] = True + + # Override service name + config.django['service_name'] = 'custom-service-name' + + +:ref:`Headers tracing ` is supported for this integration. + +.. __: https://www.djangoproject.com/ +""" # noqa: E501 +from ...utils.importlib import require_modules + + +required_modules = ["django"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from . import patch as _patch + from .patch import patch + from .patch import unpatch + + __all__ = ["patch", "unpatch", "_patch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5cb3c7339b0def6b8bbb3c8a026c5fed655eda59 GIT binary patch literal 4503 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 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/__pycache__/compat.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/__pycache__/compat.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..92702d12f8107525465d4c89dd97bc8d40be0289 GIT binary patch literal 1010 zcmZ`%O>fgc5S_J?*iD>Lkl+U5NDi%2K$RjPRH*_92_;gJa`2&%o81Pdj$_R_p^d_= zR}TCE)E@Jfa^;l2z=@f4KO#yu+1c5*GoCkZ_pn+m1C703Kf@n3z;E8H55i!drjE%- zkRpZv6byy5PK)@85IeR;c3=yT1zEg+ppfhnT8fLKQcxlkq!T+MH*m>!^9Yl_LHS3r zD$BBRAp$!o%T4L!z7!p(Jz7ykXx8kNpmoJG(IQh5OK+gE9x6SKrwaRgd^6bRpaYtk zQ|2%v(x(=E&}w!UV!0LRUO36Vsw9j0VWuQug^5cyM7v_+fkykFpjsUv$S!3K2pAv8HWA4 zwLeZWjE;Rd43ojw?~g}mnC+zJ%co4D_b)q#8{v+?#tK>L-qkA0qGX_1i3!FxLg~l=4eoI$u7tJD*z3pppYS)TK83%^lyuMhTW{4MCMtx+{CD e%F=x|8p}zn-t$#yX6M>v;aO$JBTK&JIe!6J0nj`E literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ea069b9e8b52ab9c935e7989a7ea42f1044b226b GIT binary patch literal 15300 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 c;dB!17B%<00000 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/__pycache__/restframework.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/__pycache__/restframework.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f9ede4cac07196d135e5d6492d59ec0faf01e94 GIT binary patch literal 1433 zcmZux&x_ke6rPb}S@wE2X_A)ELm5ghhQ``7A+QiiOIv6UT>`u9K~Ti3X4WgOG!k#d zc5Ix}29i^u|3FEO{g-s@Y5#?u`evj!&XNJmuQ%_#Z{GK5e%s$45^PWY{)@i~2>I78 zcAJlj@9@(O(15fr^LOhTOZL|F98D2XVMk?4!)CAp%>Kn%q2 zB}s;Yo{;h0HAW)GW7>SW1YW1-bCsFAQmI|l60U7Zv<`CJ?ucuz-MO>O%|&W}XHukP zCFX_9LL01WeH^Xx5BTY?u-niXc|+fl7vUxp^l|{7Z^Ad;TSCdk+XS25pB9g|etja1 zX68aN2~YtmrPh2ZzwVZ1RZGT|VDfn;YYUZW!?R1~kT6p@*K7_Tm0`N(YRIsi$@2;% z%Xm@bYU<8ZkWX{Pi=9Xp7`TuyaW2PWKM7q_NJd@2)CNrUwKVO`&oJ}2--Keu)wD|d zI#*4c7DHRKC!WW3-IpI3#DpyTz!_~=Y6 zXNON_-@wC1sygwl>%4y8G8Dyw#kxLPtx6dS;Sg`ftWpN@v$zEq zgVg36cqx|^Tu$m$bN?NK?bXw5RR@-~2s_dndJ&C0xP|kK+MTZ1c@ICw@Rv9-#L$^H z^98-~^cN-&p72d?gQ~moQI7%g+v9(r+cR#m3#plflWy@aRNQ3fYJZ9}T4lZGMzboj zBv|&u+zpFdE?Es#h6=IUOXRoZy;!liMwzfv8|0^VO8GSxNH3BgTZVtGmU1D%so@5c zwj6ple<{lW-F62yz+HFi)P~v8{23aw4Nry#6z*76KM)+y=xO_eeQQOnN7zN) M$Q#j-@6qAte=z#0-v9sr literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/__pycache__/utils.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/__pycache__/utils.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..04a6ef723bd592a834ae8aae162784e4741ed9cd GIT binary patch literal 5891 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 aX0mC6n_KwoX3yCTd)}_wx9ppC<9`9OwE%Dc literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/_asgi.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/_asgi.py new file mode 100644 index 000000000..17d247d44 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/_asgi.py @@ -0,0 +1,36 @@ +""" +Module providing async hooks. Do not import this module unless using Python >= 3.6. +""" +from ddtrace.contrib.asgi import span_from_scope + +from .. import trace_utils +from ...utils import get_argument_value +from .utils import REQUEST_DEFAULT_RESOURCE +from .utils import _after_request_tags +from .utils import _before_request_tags + + +@trace_utils.with_traced_module +async def traced_get_response_async(django, pin, func, instance, args, kwargs): + """Trace django.core.handlers.base.BaseHandler.get_response() (or other implementations). + + This is the main entry point for requests. + + Django requests are handled by a Handler.get_response method (inherited from base.BaseHandler). + This method invokes the middleware chain and returns the response generated by the chain. + """ + request = get_argument_value(args, kwargs, 0, "request") + span = span_from_scope(request.scope) + if span is None: + return await func(*args, **kwargs) + + # Reset the span resource so we can know if it was modified during the request or not + span.resource = REQUEST_DEFAULT_RESOURCE + _before_request_tags(pin, span, request) + response = None + try: + response = await func(*args, **kwargs) + finally: + # DEV: Always set these tags, this is where `span.resource` is set + _after_request_tags(pin, span, request, response) + return response diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/compat.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/compat.py new file mode 100644 index 000000000..8c53e7aeb --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/compat.py @@ -0,0 +1,32 @@ +import django + + +if django.VERSION >= (1, 10, 1): + from django.urls import get_resolver + + def user_is_authenticated(user): + # Explicit comparison due to the following bug + # https://code.djangoproject.com/ticket/26988 + return user.is_authenticated == True # noqa E712 + + +else: + from django.conf import settings + from django.core import urlresolvers + + def user_is_authenticated(user): + return user.is_authenticated() + + if django.VERSION >= (1, 9, 0): + + def get_resolver(urlconf=None): + urlconf = urlconf or settings.ROOT_URLCONF + urlresolvers.set_urlconf(urlconf) + return urlresolvers.get_resolver(urlconf) + + else: + + def get_resolver(urlconf=None): + urlconf = urlconf or settings.ROOT_URLCONF + urlresolvers.set_urlconf(urlconf) + return urlresolvers.RegexURLResolver(r"^/", urlconf) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/patch.py new file mode 100644 index 000000000..6d27e8253 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/patch.py @@ -0,0 +1,541 @@ +""" +The Django patching works as follows: + +Django internals are instrumented via normal `patch()`. + +`django.apps.registry.Apps.populate` is patched to add instrumentation for any +specific Django apps like Django Rest Framework (DRF). +""" +from inspect import getmro +from inspect import isclass +from inspect import isfunction +import sys + +from ddtrace import Pin +from ddtrace import config +from ddtrace.constants import SPAN_MEASURED_KEY +from ddtrace.contrib import dbapi +from ddtrace.contrib import func_name + +from ...utils import get_argument_value + + +try: + from psycopg2._psycopg import cursor as psycopg_cursor_cls + + from ddtrace.contrib.psycopg.patch import Psycopg2TracedCursor +except ImportError: + psycopg_cursor_cls = None + Psycopg2TracedCursor = None + +from ddtrace.ext import SpanTypes +from ddtrace.ext import http +from ddtrace.ext import sql as sqlx +from ddtrace.internal.compat import maybe_stringify +from ddtrace.internal.logger import get_logger +from ddtrace.utils.formats import asbool +from ddtrace.utils.formats import get_env +from ddtrace.vendor import wrapt + +from . import utils +from .. import trace_utils + + +log = get_logger(__name__) + +config._add( + "django", + dict( + _default_service="django", + cache_service_name=get_env("django", "cache_service_name") or "django", + database_service_name_prefix=get_env("django", "database_service_name_prefix", default=""), + database_service_name=get_env("django", "database_service_name", default=""), + trace_fetch_methods=asbool(get_env("django", "trace_fetch_methods", default=False)), + distributed_tracing_enabled=True, + instrument_middleware=asbool(get_env("django", "instrument_middleware", default=True)), + instrument_databases=True, + instrument_caches=True, + analytics_enabled=None, # None allows the value to be overridden by the global config + analytics_sample_rate=None, + trace_query_string=None, # Default to global config + include_user_name=True, + use_handler_resource_format=asbool(get_env("django", "use_handler_resource_format", default=False)), + use_legacy_resource_format=asbool(get_env("django", "use_legacy_resource_format", default=False)), + ), +) + + +def patch_conn(django, conn): + def cursor(django, pin, func, instance, args, kwargs): + alias = getattr(conn, "alias", "default") + + if config.django.database_service_name: + service = config.django.database_service_name + else: + database_prefix = config.django.database_service_name_prefix + service = "{}{}{}".format(database_prefix, alias, "db") + + vendor = getattr(conn, "vendor", "db") + prefix = sqlx.normalize_vendor(vendor) + tags = { + "django.db.vendor": vendor, + "django.db.alias": alias, + } + pin = Pin(service, tags=tags, tracer=pin.tracer, app=prefix) + cursor = func(*args, **kwargs) + traced_cursor_cls = dbapi.TracedCursor + if ( + Psycopg2TracedCursor is not None + and hasattr(cursor, "cursor") + and isinstance(cursor.cursor, psycopg_cursor_cls) + ): + traced_cursor_cls = Psycopg2TracedCursor + return traced_cursor_cls(cursor, pin, config.django) + + if not isinstance(conn.cursor, wrapt.ObjectProxy): + conn.cursor = wrapt.FunctionWrapper(conn.cursor, trace_utils.with_traced_module(cursor)(django)) + + +def instrument_dbs(django): + def get_connection(wrapped, instance, args, kwargs): + conn = wrapped(*args, **kwargs) + try: + patch_conn(django, conn) + except Exception: + log.debug("Error instrumenting database connection %r", conn, exc_info=True) + return conn + + if not isinstance(django.db.utils.ConnectionHandler.__getitem__, wrapt.ObjectProxy): + django.db.utils.ConnectionHandler.__getitem__ = wrapt.FunctionWrapper( + django.db.utils.ConnectionHandler.__getitem__, get_connection + ) + + +@trace_utils.with_traced_module +def traced_cache(django, pin, func, instance, args, kwargs): + if not config.django.instrument_caches: + return func(*args, **kwargs) + + # get the original function method + with pin.tracer.trace("django.cache", span_type=SpanTypes.CACHE, service=config.django.cache_service_name) as span: + # update the resource name and tag the cache backend + span.resource = utils.resource_from_cache_prefix(func_name(func), instance) + cache_backend = "{}.{}".format(instance.__module__, instance.__class__.__name__) + span._set_str_tag("django.cache.backend", cache_backend) + + if args: + keys = utils.quantize_key_values(args[0]) + span._set_str_tag("django.cache.key", str(keys)) + + return func(*args, **kwargs) + + +def instrument_caches(django): + cache_backends = set([cache["BACKEND"] for cache in django.conf.settings.CACHES.values()]) + for cache_path in cache_backends: + split = cache_path.split(".") + cache_module = ".".join(split[:-1]) + cache_cls = split[-1] + for method in ["get", "set", "add", "delete", "incr", "decr", "get_many", "set_many", "delete_many"]: + try: + cls = django.utils.module_loading.import_string(cache_path) + # DEV: this can be removed when we add an idempotent `wrap` + if not trace_utils.iswrapped(cls, method): + trace_utils.wrap(cache_module, "{0}.{1}".format(cache_cls, method), traced_cache(django)) + except Exception: + log.debug("Error instrumenting cache %r", cache_path, exc_info=True) + + +@trace_utils.with_traced_module +def traced_populate(django, pin, func, instance, args, kwargs): + """django.apps.registry.Apps.populate is the method used to populate all the apps. + + It is used as a hook to install instrumentation for 3rd party apps (like DRF). + + `populate()` works in 3 phases: + + - Phase 1: Initializes the app configs and imports the app modules. + - Phase 2: Imports models modules for each app. + - Phase 3: runs ready() of each app config. + + If all 3 phases successfully run then `instance.ready` will be `True`. + """ + + # populate() can be called multiple times, we don't want to instrument more than once + if instance.ready: + log.debug("Django instrumentation already installed, skipping.") + return func(*args, **kwargs) + + ret = func(*args, **kwargs) + + if not instance.ready: + log.debug("populate() failed skipping instrumentation.") + return ret + + settings = django.conf.settings + + # Instrument databases + if config.django.instrument_databases: + try: + instrument_dbs(django) + except Exception: + log.debug("Error instrumenting Django database connections", exc_info=True) + + # Instrument caches + if config.django.instrument_caches: + try: + instrument_caches(django) + except Exception: + log.debug("Error instrumenting Django caches", exc_info=True) + + # Instrument Django Rest Framework if it's installed + INSTALLED_APPS = getattr(settings, "INSTALLED_APPS", []) + + if "rest_framework" in INSTALLED_APPS: + try: + from .restframework import patch_restframework + + patch_restframework(django) + except Exception: + log.debug("Error patching rest_framework", exc_info=True) + + return ret + + +def traced_func(django, name, resource=None, ignored_excs=None): + """Returns a function to trace Django functions.""" + + def wrapped(django, pin, func, instance, args, kwargs): + with pin.tracer.trace(name, resource=resource) as s: + if ignored_excs: + for exc in ignored_excs: + s._ignore_exception(exc) + return func(*args, **kwargs) + + return trace_utils.with_traced_module(wrapped)(django) + + +def traced_process_exception(django, name, resource=None): + def wrapped(django, pin, func, instance, args, kwargs): + with pin.tracer.trace(name, resource=resource) as span: + resp = func(*args, **kwargs) + + # If the response code is erroneous then grab the traceback + # and set an error. + if hasattr(resp, "status_code") and 500 <= resp.status_code < 600: + span.set_traceback() + return resp + + return trace_utils.with_traced_module(wrapped)(django) + + +@trace_utils.with_traced_module +def traced_load_middleware(django, pin, func, instance, args, kwargs): + """Patches django.core.handlers.base.BaseHandler.load_middleware to instrument all middlewares.""" + settings_middleware = [] + # Gather all the middleware + if getattr(django.conf.settings, "MIDDLEWARE", None): + settings_middleware += django.conf.settings.MIDDLEWARE + if getattr(django.conf.settings, "MIDDLEWARE_CLASSES", None): + settings_middleware += django.conf.settings.MIDDLEWARE_CLASSES + + # Iterate over each middleware provided in settings.py + # Each middleware can either be a function or a class + for mw_path in settings_middleware: + mw = django.utils.module_loading.import_string(mw_path) + + # Instrument function-based middleware + if isfunction(mw) and not trace_utils.iswrapped(mw): + split = mw_path.split(".") + if len(split) < 2: + continue + base = ".".join(split[:-1]) + attr = split[-1] + + # DEV: We need to have a closure over `mw_path` for the resource name or else + # all function based middleware will share the same resource name + def _wrapper(resource): + # Function-based middleware is a factory which returns a handler function for requests. + # So instead of tracing the factory, we want to trace its returned value. + # We wrap the factory to return a traced version of the handler function. + def wrapped_factory(func, instance, args, kwargs): + # r is the middleware handler function returned from the factory + r = func(*args, **kwargs) + if r: + return wrapt.FunctionWrapper( + r, + traced_func(django, "django.middleware", resource=resource), + ) + # If r is an empty middleware function (i.e. returns None), don't wrap since + # NoneType cannot be called + else: + return r + + return wrapped_factory + + trace_utils.wrap(base, attr, _wrapper(resource=mw_path)) + + # Instrument class-based middleware + elif isclass(mw): + for hook in [ + "process_request", + "process_response", + "process_view", + "process_template_response", + "__call__", + ]: + if hasattr(mw, hook) and not trace_utils.iswrapped(mw, hook): + trace_utils.wrap( + mw, hook, traced_func(django, "django.middleware", resource=mw_path + ".{0}".format(hook)) + ) + # Do a little extra for `process_exception` + if hasattr(mw, "process_exception") and not trace_utils.iswrapped(mw, "process_exception"): + res = mw_path + ".{0}".format("process_exception") + trace_utils.wrap( + mw, "process_exception", traced_process_exception(django, "django.middleware", resource=res) + ) + + return func(*args, **kwargs) + + +@trace_utils.with_traced_module +def traced_get_response(django, pin, func, instance, args, kwargs): + """Trace django.core.handlers.base.BaseHandler.get_response() (or other implementations). + + This is the main entry point for requests. + + Django requests are handled by a Handler.get_response method (inherited from base.BaseHandler). + This method invokes the middleware chain and returns the response generated by the chain. + """ + + request = get_argument_value(args, kwargs, 0, "request") + if request is None: + return func(*args, **kwargs) + + trace_utils.activate_distributed_headers(pin.tracer, int_config=config.django, request_headers=request.META) + + with pin.tracer.trace( + "django.request", + resource=utils.REQUEST_DEFAULT_RESOURCE, + service=trace_utils.int_service(pin, config.django), + span_type=SpanTypes.WEB, + ) as span: + utils._before_request_tags(pin, span, request) + span.metrics[SPAN_MEASURED_KEY] = 1 + + response = None + try: + response = func(*args, **kwargs) + return response + finally: + # DEV: Always set these tags, this is where `span.resource` is set + utils._after_request_tags(pin, span, request, response) + + +@trace_utils.with_traced_module +def traced_template_render(django, pin, wrapped, instance, args, kwargs): + """Instrument django.template.base.Template.render for tracing template rendering.""" + template_name = maybe_stringify(getattr(instance, "name", None)) + if template_name: + resource = template_name + else: + resource = "{0}.{1}".format(func_name(instance), wrapped.__name__) + + with pin.tracer.trace("django.template.render", resource=resource, span_type=http.TEMPLATE) as span: + if template_name: + span._set_str_tag("django.template.name", template_name) + engine = getattr(instance, "engine", None) + if engine: + span._set_str_tag("django.template.engine.class", func_name(engine)) + + return wrapped(*args, **kwargs) + + +def instrument_view(django, view): + """ + Helper to wrap Django views. + + We want to wrap all lifecycle/http method functions for every class in the MRO for this view + """ + if hasattr(view, "__mro__"): + for cls in reversed(getmro(view)): + _instrument_view(django, cls) + + return _instrument_view(django, view) + + +def _instrument_view(django, view): + """Helper to wrap Django views.""" + # All views should be callable, double check before doing anything + if not callable(view): + return view + + # Patch view HTTP methods and lifecycle methods + http_method_names = getattr(view, "http_method_names", ("get", "delete", "post", "options", "head")) + lifecycle_methods = ("setup", "dispatch", "http_method_not_allowed") + for name in list(http_method_names) + list(lifecycle_methods): + try: + func = getattr(view, name, None) + if not func or isinstance(func, wrapt.ObjectProxy): + continue + + resource = "{0}.{1}".format(func_name(view), name) + op_name = "django.view.{0}".format(name) + trace_utils.wrap(view, name, traced_func(django, name=op_name, resource=resource)) + except Exception: + log.debug("Failed to instrument Django view %r function %s", view, name, exc_info=True) + + # Patch response methods + response_cls = getattr(view, "response_class", None) + if response_cls: + methods = ("render",) + for name in methods: + try: + func = getattr(response_cls, name, None) + # Do not wrap if the method does not exist or is already wrapped + if not func or isinstance(func, wrapt.ObjectProxy): + continue + + resource = "{0}.{1}".format(func_name(response_cls), name) + op_name = "django.response.{0}".format(name) + trace_utils.wrap(response_cls, name, traced_func(django, name=op_name, resource=resource)) + except Exception: + log.debug("Failed to instrument Django response %r function %s", response_cls, name, exc_info=True) + + # If the view itself is not wrapped, wrap it + if not isinstance(view, wrapt.ObjectProxy): + view = wrapt.FunctionWrapper( + view, traced_func(django, "django.view", resource=func_name(view), ignored_excs=[django.http.Http404]) + ) + return view + + +@trace_utils.with_traced_module +def traced_urls_path(django, pin, wrapped, instance, args, kwargs): + """Wrapper for url path helpers to ensure all views registered as urls are traced.""" + try: + if "view" in kwargs: + kwargs["view"] = instrument_view(django, kwargs["view"]) + elif len(args) >= 2: + args = list(args) + args[1] = instrument_view(django, args[1]) + args = tuple(args) + except Exception: + log.debug("Failed to instrument Django url path %r %r", args, kwargs, exc_info=True) + return wrapped(*args, **kwargs) + + +@trace_utils.with_traced_module +def traced_as_view(django, pin, func, instance, args, kwargs): + """ + Wrapper for django's View.as_view class method + """ + try: + instrument_view(django, instance) + except Exception: + log.debug("Failed to instrument Django view %r", instance, exc_info=True) + view = func(*args, **kwargs) + return wrapt.FunctionWrapper(view, traced_func(django, "django.view", resource=func_name(view))) + + +@trace_utils.with_traced_module +def traced_get_asgi_application(django, pin, func, instance, args, kwargs): + from ddtrace.contrib.asgi import TraceMiddleware + + def django_asgi_modifier(span, scope): + span.name = "django.request" + + return TraceMiddleware(func(*args, **kwargs), integration_config=config.django, span_modifier=django_asgi_modifier) + + +def _patch(django): + Pin().onto(django) + trace_utils.wrap(django, "apps.registry.Apps.populate", traced_populate(django)) + + # DEV: this check will be replaced with import hooks in the future + if "django.core.handlers.base" not in sys.modules: + import django.core.handlers.base + + if config.django.instrument_middleware: + trace_utils.wrap(django, "core.handlers.base.BaseHandler.load_middleware", traced_load_middleware(django)) + + trace_utils.wrap(django, "core.handlers.base.BaseHandler.get_response", traced_get_response(django)) + if hasattr(django.core.handlers.base.BaseHandler, "get_response_async"): + # Have to inline this import as the module contains syntax incompatible with Python 3.5 and below + from ._asgi import traced_get_response_async + + trace_utils.wrap(django, "core.handlers.base.BaseHandler.get_response_async", traced_get_response_async(django)) + + # Only wrap get_asgi_application if get_response_async exists. Otherwise we will effectively double-patch + # because get_response and get_asgi_application will be used. + if "django.core.asgi" not in sys.modules: + try: + import django.core.asgi + except ImportError: + pass + else: + trace_utils.wrap(django, "core.asgi.get_asgi_application", traced_get_asgi_application(django)) + + # DEV: this check will be replaced with import hooks in the future + if "django.template.base" not in sys.modules: + import django.template.base + trace_utils.wrap(django, "template.base.Template.render", traced_template_render(django)) + + # DEV: this check will be replaced with import hooks in the future + if "django.conf.urls.static" not in sys.modules: + import django.conf.urls.static + trace_utils.wrap(django, "conf.urls.url", traced_urls_path(django)) + if django.VERSION >= (2, 0, 0): + trace_utils.wrap(django, "urls.path", traced_urls_path(django)) + trace_utils.wrap(django, "urls.re_path", traced_urls_path(django)) + + # DEV: this check will be replaced with import hooks in the future + if "django.views.generic.base" not in sys.modules: + import django.views.generic.base + trace_utils.wrap(django, "views.generic.base.View.as_view", traced_as_view(django)) + + +def patch(): + # DEV: this import will eventually be replaced with the module given from an import hook + import django + + if django.VERSION < (1, 10, 0): + utils.Resolver404 = django.core.urlresolvers.Resolver404 + else: + utils.Resolver404 = django.urls.exceptions.Resolver404 + + utils.DJANGO22 = django.VERSION >= (2, 2, 0) + + if getattr(django, "_datadog_patch", False): + return + _patch(django) + + setattr(django, "_datadog_patch", True) + + +def _unpatch(django): + trace_utils.unwrap(django.apps.registry.Apps, "populate") + trace_utils.unwrap(django.core.handlers.base.BaseHandler, "load_middleware") + trace_utils.unwrap(django.core.handlers.base.BaseHandler, "get_response") + trace_utils.unwrap(django.core.handlers.base.BaseHandler, "get_response_async") + trace_utils.unwrap(django.template.base.Template, "render") + trace_utils.unwrap(django.conf.urls.static, "static") + trace_utils.unwrap(django.conf.urls, "url") + if django.VERSION >= (2, 0, 0): + trace_utils.unwrap(django.urls, "path") + trace_utils.unwrap(django.urls, "re_path") + trace_utils.unwrap(django.views.generic.base.View, "as_view") + for conn in django.db.connections.all(): + trace_utils.unwrap(conn, "cursor") + trace_utils.unwrap(django.db.utils.ConnectionHandler, "__getitem__") + + +def unpatch(): + import django + + if not getattr(django, "_datadog_patch", False): + return + + _unpatch(django) + + setattr(django, "_datadog_patch", False) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/restframework.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/restframework.py new file mode 100644 index 000000000..6bab20ca2 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/restframework.py @@ -0,0 +1,33 @@ +import rest_framework.views + +from ddtrace.vendor.wrapt import wrap_function_wrapper as wrap + +from ...utils.wrappers import iswrapped +from ..trace_utils import with_traced_module + + +@with_traced_module +def _traced_handle_exception(django, pin, wrapped, instance, args, kwargs): + """Sets the error message, error type and exception stack trace to the current span + before calling the original exception handler. + """ + span = pin.tracer.current_span() + + if span is not None: + span.set_traceback() + + return wrapped(*args, **kwargs) + + +def patch_restframework(django): + """Patches rest_framework app. + + To trace exceptions occurring during view processing we currently use a TraceExceptionMiddleware. + However the rest_framework handles exceptions before they come to our middleware. + So we need to manually patch the rest_framework exception handler + to set the exception stack trace in the current span. + """ + + # trace the handle_exception method + if not iswrapped(rest_framework.views.APIView, "handle_exception"): + wrap("rest_framework.views", "APIView.handle_exception", _traced_handle_exception(django)) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/utils.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/utils.py new file mode 100644 index 000000000..f39f63d28 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/django/utils.py @@ -0,0 +1,296 @@ +from django.utils.functional import SimpleLazyObject +import six + +from ddtrace import config +from ddtrace.constants import ANALYTICS_SAMPLE_RATE_KEY +from ddtrace.constants import SPAN_MEASURED_KEY +from ddtrace.contrib import func_name +from ddtrace.ext import SpanTypes +from ddtrace.propagation.utils import from_wsgi_header + +from .. import trace_utils +from ...internal.logger import get_logger +from .compat import get_resolver +from .compat import user_is_authenticated + + +log = get_logger(__name__) + + +# Set on patch, when django is imported +Resolver404 = None +DJANGO22 = None + +REQUEST_DEFAULT_RESOURCE = "__django_request" + + +def resource_from_cache_prefix(resource, cache): + """ + Combine the resource name with the cache prefix (if any) + """ + if getattr(cache, "key_prefix", None): + name = " ".join((resource, cache.key_prefix)) + else: + name = resource + + # enforce lowercase to make the output nicer to read + return name.lower() + + +def quantize_key_values(key): + """ + Used in the Django trace operation method, it ensures that if a dict + with values is used, we removes the values from the span meta + attributes. For example:: + + >>> quantize_key_values({'key': 'value'}) + # returns ['key'] + """ + if isinstance(key, dict): + return key.keys() + + return key + + +def get_django_2_route(request, resolver_match): + # Try to use `resolver_match.route` if available + # Otherwise, look for `resolver.pattern.regex.pattern` + route = resolver_match.route + if route: + return route + + resolver = get_resolver(getattr(request, "urlconf", None)) + if resolver: + try: + return resolver.pattern.regex.pattern + except AttributeError: + pass + + return None + + +def set_tag_array(span, prefix, value): + """Helper to set a span tag as a single value or an array""" + if not value: + return + + if len(value) == 1: + if value[0]: + span._set_str_tag(prefix, value[0]) + else: + for i, v in enumerate(value, start=0): + if v: + span._set_str_tag("".join((prefix, ".", str(i))), v) + + +def get_request_uri(request): + """ + Helper to rebuild the original request url + + query string or fragments are not included. + """ + # DEV: Use django.http.request.HttpRequest._get_raw_host() when available + # otherwise back-off to PEP 333 as done in django 1.8.x + if hasattr(request, "_get_raw_host"): + host = request._get_raw_host() + else: + try: + # Try to build host how Django would have + # https://github.com/django/django/blob/e8d0d2a5efc8012dcc8bf1809dec065ebde64c81/django/http/request.py#L85-L102 + if "HTTP_HOST" in request.META: + host = request.META["HTTP_HOST"] + else: + host = request.META["SERVER_NAME"] + port = str(request.META["SERVER_PORT"]) + if port != ("443" if request.is_secure() else "80"): + host = "".join((host, ":", port)) + except Exception: + # This really shouldn't ever happen, but lets guard here just in case + log.debug("Failed to build Django request host", exc_info=True) + host = "unknown" + + # If request scheme is missing, possible in case where wsgi.url_scheme + # environ has not been set, return None and skip providing a uri + if request.scheme is None: + return + + # Build request url from the information available + # DEV: We are explicitly omitting query strings since they may contain sensitive information + urlparts = {"scheme": request.scheme, "netloc": host, "path": request.path} + + # If any url part is a SimpleLazyObject, use its __class__ property to cast + # str/bytes and allow for _setup() to execute + for (k, v) in urlparts.items(): + if isinstance(v, SimpleLazyObject): + if issubclass(v.__class__, str): + v = str(v) + elif issubclass(v.__class__, bytes): + v = bytes(v) + else: + # lazy object that is not str or bytes should not happen here + # but if it does skip providing a uri + log.debug( + "Skipped building Django request uri, %s is SimpleLazyObject wrapping a %s class", + k, + v.__class__.__name__, + ) + return None + urlparts[k] = six.ensure_text(v) + + return "".join((urlparts["scheme"], "://", urlparts["netloc"], urlparts["path"])) + + +def _set_resolver_tags(pin, span, request): + # Default to just the HTTP method when we cannot determine a reasonable resource + resource = request.method + + try: + # Get resolver match result and build resource name pieces + resolver_match = request.resolver_match + if not resolver_match: + # The request quite likely failed (e.g. 404) so we do the resolution anyway. + resolver = get_resolver(getattr(request, "urlconf", None)) + resolver_match = resolver.resolve(request.path_info) + handler = func_name(resolver_match[0]) + + if config.django.use_handler_resource_format: + resource = " ".join((request.method, handler)) + elif config.django.use_legacy_resource_format: + resource = handler + else: + # In Django >= 2.2.0 we can access the original route or regex pattern + # TODO: Validate if `resolver.pattern.regex.pattern` is available on django<2.2 + if DJANGO22: + # Determine the resolver and resource name for this request + route = get_django_2_route(request, resolver_match) + if route: + resource = " ".join((request.method, route)) + span._set_str_tag("http.route", route) + else: + resource = " ".join((request.method, handler)) + + span._set_str_tag("django.view", resolver_match.view_name) + set_tag_array(span, "django.namespace", resolver_match.namespaces) + + # Django >= 2.0.0 + if hasattr(resolver_match, "app_names"): + set_tag_array(span, "django.app", resolver_match.app_names) + + except Resolver404: + # Normalize all 404 requests into a single resource name + # DEV: This is for potential cardinality issues + resource = " ".join((request.method, "404")) + except Exception: + log.debug( + "Failed to resolve request path %r with path info %r", + request, + getattr(request, "path_info", "not-set"), + exc_info=True, + ) + finally: + # Only update the resource name if it was not explicitly set + # by anyone during the request lifetime + if span.resource == REQUEST_DEFAULT_RESOURCE: + span.resource = resource + + +def _before_request_tags(pin, span, request): + # DEV: Do not set `span.resource` here, leave it as `None` + # until `_set_resolver_tags` so we can know if the user + # has explicitly set it during the request lifetime + span.service = trace_utils.int_service(pin, config.django) + span.span_type = SpanTypes.WEB + span.metrics[SPAN_MEASURED_KEY] = 1 + + analytics_sr = config.django.get_analytics_sample_rate(use_global_config=True) + if analytics_sr is not None: + span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, analytics_sr) + + span._set_str_tag("django.request.class", func_name(request)) + + +def _after_request_tags(pin, span, request, response): + # Response can be None in the event that the request failed + # We still want to set additional request tags that are resolved + # during the request. + + try: + user = getattr(request, "user", None) + if user is not None: + # Note: getattr calls to user / user_is_authenticated may result in ImproperlyConfigured exceptions from + # Django's get_user_model(): + # https://github.com/django/django/blob/a464ead29db8bf6a27a5291cad9eb3f0f3f0472b/django/contrib/auth/__init__.py + try: + if hasattr(user, "is_authenticated"): + span._set_str_tag("django.user.is_authenticated", str(user_is_authenticated(user))) + + uid = getattr(user, "pk", None) + if uid: + span._set_str_tag("django.user.id", str(uid)) + + if config.django.include_user_name: + username = getattr(user, "username", None) + if username: + span._set_str_tag("django.user.name", username) + except Exception: + log.debug("Error retrieving authentication information for user %r", user, exc_info=True) + + if response: + status = response.status_code + span._set_str_tag("django.response.class", func_name(response)) + if hasattr(response, "template_name"): + # template_name is a bit of a misnomer, as it could be any of: + # a list of strings, a tuple of strings, a single string, or an instance of Template + # for more detail, see: + # https://docs.djangoproject.com/en/3.0/ref/template-response/#django.template.response.SimpleTemplateResponse.template_name + template = response.template_name + + if isinstance(template, six.string_types): + template_names = [template] + elif isinstance( + template, + ( + list, + tuple, + ), + ): + template_names = template + elif hasattr(template, "template"): + # ^ checking by attribute here because + # django backend implementations don't have a common base + # `.template` is also the most consistent across django versions + template_names = [template.template.name] + else: + template_names = None + + set_tag_array(span, "django.response.template", template_names) + + url = get_request_uri(request) + + if DJANGO22: + request_headers = request.headers + else: + request_headers = {} + for header, value in request.META.items(): + name = from_wsgi_header(header) + if name: + request_headers[name] = value + + # DEV: Resolve the view and resource name at the end of the request in case + # urlconf changes at any point during the request + _set_resolver_tags(pin, span, request) + + response_headers = dict(response.items()) if response else {} + trace_utils.set_http_meta( + span, + config.django, + method=request.method, + url=url, + status_code=status, + query=request.META.get("QUERY_STRING", None), + request_headers=request_headers, + response_headers=response_headers, + ) + finally: + if span.resource == REQUEST_DEFAULT_RESOURCE: + span.resource = request.method diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/__init__.py new file mode 100644 index 000000000..d45bb6162 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/__init__.py @@ -0,0 +1,49 @@ +""" +Instrument dogpile.cache__ to report all cached lookups. + +This will add spans around the calls to your cache backend (e.g. redis, memory, +etc). The spans will also include the following tags: + +- key/keys: The key(s) dogpile passed to your backend. Note that this will be + the output of the region's ``function_key_generator``, but before any key + mangling is applied (i.e. the region's ``key_mangler``). +- region: Name of the region. +- backend: Name of the backend class. +- hit: If the key was found in the cache. +- expired: If the key is expired. This is only relevant if the key was found. + +While cache tracing will generally already have keys in tags, some caching +setups will not have useful tag values - such as when you're using consistent +hashing with memcached - the key(s) will appear as a mangled hash. +:: + + # Patch before importing dogpile.cache + from ddtrace import patch + patch(dogpile_cache=True) + + from dogpile.cache import make_region + + region = make_region().configure( + "dogpile.cache.pylibmc", + expiration_time=3600, + arguments={"url": ["127.0.0.1"]}, + ) + + @region.cache_on_arguments() + def hello(name): + # Some complicated, slow calculation + return "Hello, {}".format(name) + +.. __: https://dogpilecache.sqlalchemy.org/ +""" +from ...utils.importlib import require_modules + + +required_modules = ["dogpile.cache"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + from .patch import unpatch + + __all__ = ["patch", "unpatch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fe83edb946e3b4452c90d74f5ae28eed64783604 GIT binary patch literal 1772 zcmZuy!EPHj5M9ZV?JVp%m);2lXrw~km6O(mg*eEehvs6miHj5}iZ!(y?K0+)8?gA>X2M1QPH)u;x1?2yqS4J|9E&9M|hq7`3F7SjiSHZ!>89h ze0YXm^8#-ynnkR`x}sBcXWdT3c8bny2fDEHTj%#LXFcedZ&{D^MZfCL`nca?ySUz+ z?c#cm?HAqI-mB>A=!fV<^i#y{y^KaN{k$JV@pEacZYq$LFjdrCK$6qEge)Ug5e>D{ zmQW#xf6Ita>bj{-633S%H{^!n7G;c>no2{cR*huDmVov`7>BV^jSk*qPV;NP{UIbp zg5ZpsF{z+ZdNq#0=A(pMqHDkk(84IfWiA>99(JLGP&Zr_#L~h{J#oIcMImE;VQH`w|OVq+YX(+go4-L7xS~N1Z_>f_g ztN;m`TBWb9#$=B6b66-1gvyoUgjf}og>YOk0S%?0M{tV(XD>OV7af)LFvYnqxb*tdVp3+I0n= z@S~(yAvFO@ih|+)1C7FXRicX8j$2K0R{-D4P%wcIRA``VMM}B^S1SZZM%d6XF$xoQ z#Lz7^U{P$rTPhpu8v~0*ICHY3q5(sWiD~kZV0t$tNT;}m*oVfr$hne+8;b&uOKKb{ z!r9Wvu9ft$uLZJjx(TYQYoOZU(a=Nen{!U$DJqWO^?-a&Erx7c&MP-34tr~eyy-%# ziZJFX0VEirD$&{R`qi-Y$~>dPbZU)yHpqcz%b|U28|YjDfxMD^5~mn zf`2E2U)~4HHi2h>R(q)62=)IHqLc)CUxqy4+REMqFq zve{8%xiCqP0G3>L(PDPL4Z_w@W=9n_PP*$CAK@Gp9;;dQSETn{40rqI*RtfB>}?^>&ThY&xT~ns%e3b8F~@%Zh)7jXS;0LFb_RFM{tg A3IG5A literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/__pycache__/lock.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/__pycache__/lock.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f8dbb9b38aaf936b950255933b2464fb6d6e6167 GIT binary patch literal 1314 zcmZ`(&5s*36u0Lq*=({c6lpo2%C{2KCfkB4tyEQjL{HU%w7Y-?q>(dzNt~V7BiqS# z!{owN>T*Hi$^m5&m+~jgl~ex(PCQRTg(~qze$OAzzh}Sq^PKj29fI=kw_o^o2_b*^ z!G9&d!Kc{lT~vYyT9A~8K!gRI2WdckKPtj`lt$Q(Me>}av7pDK-+F~R$kD41b)VMZ zS5l!#xE*O-Fr!7wevfPKDj%xUHf&(GLYI(wI$15*IPPnX=~Q->LPfA$;`dSON0YiTS6Ys^9V1>@l?0}uBu{Lg@xP#(?Nq5IG&hkRr zg#jjDEEU2_Wu<@y*GHXz%e2XI1GwuWHU~G=0%J_^8c-|&PiwV5aL&~o$ADXj*#Dkd zTNpG@Ip*w*U=>LZ+bwMNPgIU(;h9b)55j3TX=z9~B_d#8(_l+RYcnfO45Fxk_6WH#$Bs zo_=Y|!{De6c`4KO(U;lRpFKVOBHhiT&9;&INgb9_8P8GELRCi|sg9StSU|e_x^%+H zIlK(&HrCg1l|y<<8<~xebf82w2BaC7m*npshNY29EcS58&2X#>0mcqj_lJigbv`?I zID263f2_)b?Ab&e9cfC&N;f_Zsw~xVK!DhZh%f7z)u`^Nr25az>7Z+pJ_w zQih(OgYu%@-;(_8bKS&i*$+?=+QlykA{xRlo53UO zVpSdw{s;D`Pt*6BG-dcqwj{@7sD4~W{+$^gxQ@7)*g82o_ttpUns>aq;nlm|rvR}D>i3nvRZz-Kw<&Nek~JYVBCK^oNrX+N54u_)j% P{!obZkEk7BHo|`Z#^7z7 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0ba7c71d67a9126c50992ea956ff0625b7c0bb30 GIT binary patch literal 1431 zcmZ`(OK;Oa5cVsvR;f*%sOcr1Z(-*Z)P8!o%z%euQOKt!+h#`3+X^fi01m0w3VvSm)CApQ@qc&+v-U99rM^!>4 zc4Is4#0zBcoG+1O;8wg6cgZTVS^JYh)|kUw=r1vKU+HzuATDLlQ!9NpN}+S;qepc2 z>3|L%K7S1M^3>kld-3|s)Vung`#C*4P6H8TDMc#}`I!oP!I}l>ph*_Iqk+gcxE5>I zFcdW4p)W%4t^MtzqvJ$G0Y-GO8DQ+dTf7Ajicn*XsZ2Z7`7%@X6`@Z&`YGg47|dV| z(KzQ#(VTFTS;CauVl8;=&)WAEuKl56z9Ad!LDiwm7e32|H1|bt^yFpP1tX7=upjur zQP}6<5RP1~@1V6OjXoYLH~%|SAFy<$J7R`P_ZeWH(kP7trKLL^l%MNqM2FlG zvk3V(3c{XUnea7V2ws_!nN>qhtzb?)ph03S$Q5rPmn-14>$()aP^-S`&Yx}D~m^usXS-Y)NKVjJe(OD~V|jVLXIpClXO zGT%Nq8HFBWg8RX&58#75I`AfeQW>Ww6Wh;Es)l6C5J=GhP}GiUs2$Bw;n6ty|F4Au z7`>>4VqOcN4%7lxH*UmDY6|O|w}cJ69Xr&mwIe!Ak@KaJ#6oRJMd$;L7yLruYUJ1r~ggVgplbL%R8O@bxasW0CfxeEmF1rC?^elkRkE9HuPeeZ294 zH()hiK)8Y+mDWWzQ=iP$D*DzCRuC>DTtc{ta1CJ{0ZT4J`sZn^d!vjUC*ghk%@nwa QRci&*A~mDSW9gRj2R8#_Q2+n{ literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/__pycache__/region.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/__pycache__/region.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5d51c35248ccaf4bd69c6672fe279e9230aa9580 GIT binary patch literal 1382 zcmbVLTW{P%6rS;Qytk&IgmN$CX&)M=Tq;FEh$uut0;1Aw1^8v;cqWS-+iQ(ylWycZ z!7CtM5kiT0>R*~yp86O10_V)GLa2mPF)M%Pn=`Y==koc{aM&ZT?*0Bt`j&GD=+aZ_e;MDlECty3@er};GtkYkR%e3=z;AC zIw9l!Q;10R$J9iVCaoVX8reQ|!3Cb{llwdS{L8(a$)g8*pYqT5z6GayKd-^8Po?Im zn$9a(Ykr)TbD2Trmn#H@HSNOF{sy4QIn_?#UeGhI^VH4GJNGU~d%Gi@)44xEF4{*X zsBpgb&iu|Rf+EDaG%F(E|LFd_e-?DXc~AE*C?&{*)A<)rz2!kii^1hCgx#=puB1?JEj78uv&po(;up+T^9&%+l;TnGCVgnc|s}sg_S1 z!<1}ibCt;?YN2IZL(fd0Oh{X>s7j-`&~$Y|n%(gwW|!VSkNRr^~IL9lT zk3-`f&g;yuyl!<`XVQ2uvDO4fC)T=6UMn1w@vx95M7<50XJ>Jv@?)qqK2BA9I4gxz zZG3t+-ae>LimiLad+P4{b+g4EPwVaN(+{?AO`G^(+~mz2`>wLQb9~xtFBX-Ig|H=E z+H6*9l^?{bc5vH`c-g+qW?|O&iAo!eeOtC1Wz^mPAanq~o-?8wvCOY)wTeNq$xHu|8?WZ2 z&eb(IQ4vBPVE`~5S%Ik`Iz|Zg(i$>Ul)8?vj)1+h;!!Un+dy~);U>U1RIeg`4dHc! zHxS-LxQ*}@0t#aZw}6?A=V!Fc6XQKMsT==H>RmV;_ie68f2E2|`B*C}_6T%=D%I^W wRhxkQg#5YA%XV25PFrE1D;e3`Zh_CRkE)rNm-1u$m|N@;a|RBIX5g~X-$8FpR{#J2 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/lock.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/lock.py new file mode 100644 index 000000000..5e293c052 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/lock.py @@ -0,0 +1,39 @@ +import dogpile + +from ...pin import Pin +from ...utils.formats import asbool + + +def _wrap_lock_ctor(func, instance, args, kwargs): + """ + This seems rather odd. But to track hits, we need to patch the wrapped function that + dogpile passes to the region and locks. Unfortunately it's a closure defined inside + the get_or_create* methods themselves, so we can't easily patch those. + """ + func(*args, **kwargs) + ori_backend_fetcher = instance.value_and_created_fn + + def wrapped_backend_fetcher(): + pin = Pin.get_from(dogpile.cache) + if not pin or not pin.enabled(): + return ori_backend_fetcher() + + hit = False + expired = True + try: + value, createdtime = ori_backend_fetcher() + hit = value is not dogpile.cache.api.NO_VALUE + # dogpile sometimes returns None, but only checks for truthiness. Coalesce + # to minimize APM users' confusion. + expired = instance._is_expired(createdtime) or False + return value, createdtime + finally: + # Keys are checked in random order so the 'final' answer for partial hits + # should really be false (ie. if any are 'negative', then the tag value + # should be). This means ANDing all hit values and ORing all expired values. + span = pin.tracer.current_span() + if span: + span.set_tag("hit", asbool(span.get_tag("hit") or "True") and hit) + span.set_tag("expired", asbool(span.get_tag("expired") or "False") or expired) + + instance.value_and_created_fn = wrapped_backend_fetcher diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/patch.py new file mode 100644 index 000000000..65cae90ad --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/patch.py @@ -0,0 +1,41 @@ +import dogpile + +from ddtrace.pin import Pin +from ddtrace.pin import _DD_PIN_NAME +from ddtrace.pin import _DD_PIN_PROXY_NAME +from ddtrace.vendor.wrapt import wrap_function_wrapper as _w + +from .lock import _wrap_lock_ctor +from .region import _wrap_get_create +from .region import _wrap_get_create_multi + + +_get_or_create = dogpile.cache.region.CacheRegion.get_or_create +_get_or_create_multi = dogpile.cache.region.CacheRegion.get_or_create_multi +_lock_ctor = dogpile.lock.Lock.__init__ + + +def patch(): + if getattr(dogpile.cache, "_datadog_patch", False): + return + setattr(dogpile.cache, "_datadog_patch", True) + + _w("dogpile.cache.region", "CacheRegion.get_or_create", _wrap_get_create) + _w("dogpile.cache.region", "CacheRegion.get_or_create_multi", _wrap_get_create_multi) + _w("dogpile.lock", "Lock.__init__", _wrap_lock_ctor) + + Pin(app="dogpile.cache", service="dogpile.cache").onto(dogpile.cache) + + +def unpatch(): + if not getattr(dogpile.cache, "_datadog_patch", False): + return + setattr(dogpile.cache, "_datadog_patch", False) + # This looks silly but the unwrap util doesn't support class instance methods, even + # though wrapt does. This was causing the patches to stack on top of each other + # during testing. + dogpile.cache.region.CacheRegion.get_or_create = _get_or_create + dogpile.cache.region.CacheRegion.get_or_create_multi = _get_or_create_multi + dogpile.lock.Lock.__init__ = _lock_ctor + setattr(dogpile.cache, _DD_PIN_NAME, None) + setattr(dogpile.cache, _DD_PIN_PROXY_NAME, None) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/region.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/region.py new file mode 100644 index 000000000..e60efa7c6 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/dogpile_cache/region.py @@ -0,0 +1,35 @@ +import dogpile + +from ddtrace.ext import SpanTypes + +from ...constants import SPAN_MEASURED_KEY +from ...pin import Pin +from ...utils import get_argument_value + + +def _wrap_get_create(func, instance, args, kwargs): + pin = Pin.get_from(dogpile.cache) + if not pin or not pin.enabled(): + return func(*args, **kwargs) + + key = get_argument_value(args, kwargs, 0, "key") + with pin.tracer.trace("dogpile.cache", resource="get_or_create", span_type=SpanTypes.CACHE) as span: + span.set_tag(SPAN_MEASURED_KEY) + span.set_tag("key", key) + span.set_tag("region", instance.name) + span.set_tag("backend", instance.actual_backend.__class__.__name__) + return func(*args, **kwargs) + + +def _wrap_get_create_multi(func, instance, args, kwargs): + pin = Pin.get_from(dogpile.cache) + if not pin or not pin.enabled(): + return func(*args, **kwargs) + + keys = get_argument_value(args, kwargs, 0, "keys") + with pin.tracer.trace("dogpile.cache", resource="get_or_create_multi", span_type="cache") as span: + span.set_tag(SPAN_MEASURED_KEY) + span.set_tag("keys", keys) + span.set_tag("region", instance.name) + span.set_tag("backend", instance.actual_backend.__class__.__name__) + return func(*args, **kwargs) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/elasticsearch/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/elasticsearch/__init__.py new file mode 100644 index 000000000..8a5e1590f --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/elasticsearch/__init__.py @@ -0,0 +1,25 @@ +"""Instrument Elasticsearch to report Elasticsearch queries. + +``patch_all`` will automatically patch your Elasticsearch instance to make it work. +:: + + from ddtrace import Pin, patch + from elasticsearch import Elasticsearch + + # If not patched yet, you can patch elasticsearch specifically + patch(elasticsearch=True) + + # This will report spans with the default instrumentation + es = Elasticsearch(port=ELASTICSEARCH_CONFIG['port']) + # Example of instrumented query + es.indices.create(index='books', ignore=400) + + # Use a pin to specify metadata related to this client + es = Elasticsearch(port=ELASTICSEARCH_CONFIG['port']) + Pin.override(es.transport, service='elasticsearch-videos') + es.indices.create(index='videos', ignore=400) +""" +from .patch import patch + + +__all__ = ["patch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/elasticsearch/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/elasticsearch/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5b650d6d0080677b2e2c030158ca6bd830d59047 GIT binary patch literal 1049 zcmbtT!EO^V5Z!HBfg*b3!pTw(Nu)HJ0;-gXAgU@rBvcShFGa|4*OSDp*EY5{?e@Yq z@B`fWrCvGl32ul&TVPVAQV2;hb`C^%a-r>cuerC^ zY!bAY)fwU3TZU00E9-bCRJX$X8v?bOSAjJm72XyZ&xq2#x(}Qb;JcwTk}|a_w2tKr zq+(VvDf5+V+O?qJ#OBcXlVP$Du99IToHI;?9gk`*fN+?xTzV4NSCXSrT9x47$gnO_ zJ8&M3-s~Su#s^2E{r3m2>A~B>m*ZC-TH&bmu~XjMBBM)|83{zsZb2i((5YgD-V=(8 z6x)_sU>@4&!sW1as`a^Rb%~fOZDIJd-@jpV?0_(0gbGcn#3)Dxo^j?Gj)p`S9?pEA zol1ct^q*9)!9BeMYXyfk){5O%E^zG<2X=||4_h_;x0e{HU8_<8Z~U9(DymNNM~u&- z6JKwXeYN4$9CjMl_bBB$rL^1#9!@f)-y*w#0p;@Rb7HJmBK>5^Y%BH_0S1!fcnbFXi^~$_$DkgJd=3H~&;CZ^da+W13JZ bl<<`HOmThxS*Cd|;d!9o)^S;X_@MC{1u;*f literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/elasticsearch/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/elasticsearch/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bbcd004985dacda63bc41d6b0e20f49ba99eb5ec GIT binary patch literal 3203 zcmbUjO>^7Ebr%3Z@KYa_XiK&e$4Q*9osf1KyW=*=l$Gej6PZ#(Dz-50U?46elLA0^ zv6L+i*hA@=_L4UJ10894a?kw*ogVuGI(zNOJ$8EPsqZZ)S5nn_UnD_dvB{$ z$`knR{O-4|JwwRf5IOy6fcykn{AcJGVbmu9wJAkd^R+;?bp`9b5oBx=V1s4+Y+%`z zf=z&Pc22=rKOYqAf`To8i*`xDIe^P{S;2XKCYZHn6)I8z%y+21+g!(IW`aDC3b-=z_`Q~Sp~*rc9C6z@iJRt%P?MHm)oRzk2jOmS51_?o+>M;wVb~SDLFgbBdHj{09$ad)8f)8~`zvjy-FUFE z)^s)-ou>0ua~qD%wKp0q=Rvd4-r8)gPA7Bi$PGJ(kr%%Nx}NXGqSuW*mv^7Q5#xyv z5sbMZ_r0(?V91QV(FM2zEXLU^8v4Fb}?1G6S{#H%fcz_CE*v~z`v4u#Cr#>@ahLHukQ|g=JB{b zx>dis6F%$Tywm@f-}*rq-E^Msg?I0cKDmkCUR3YYqh9n;FN}rj`yU;QqPvHOfmdft zaJT!P5ezPPZ>N4n+Ir-Q?vq+{C<~4kPu(M)19svfbc7m~MKxN6pDzM_#uR8Lbdwcm z@g?X4nV=zxV^DiSCh&5enyBoTv;|th^U`z_MUEy^VyE2#u4UBHbpT@nIzsc5UA7I)<=d(x*NdvA=;IRrVA$^@0!a#P^Oh%afEHsN{zSWL$xZckn zt49uAVYX!1VXko5U@ztLUT3t_;cgh?`>Dap*d6e|;oj4s7mKPUv$4kydR@=f;oVdV zGP~yqR|t*^DWEyeWDabRULhA8*x{VJh?hVTX$5cusoU9hbMu?~D@|K(Y;4#Dm~J3* zui1E$_V`7(;R9%~3Uaha4QTUJ&HsO9;}y8>#Bi1~4F9+3veU&nGVt1X?*jH;#=})* zyzc>U%6K=Xh66*M7_M)C88wJ~_+X6|UxE|&?3r`At7iCRSm0NnlbSQ+Zz1*$c5m=9 zj{fxtwAh4>fR2h(&7ENsU$8!H@hkwpB)b&7?1eR^2?ciaR#(XhYxv=w0yKQd@jt*x z1v!F^$8>BY2CpQ>k#P4autMWQ{{#^yMi=tp^`AJZu6k+CHTi8Z0)oSHR&avo?DlZ*om zZY}l8iFpj#oMJOc_7pao2i_*ZZf0ti45c7LlWLI$gfEMO8@G9{yWMaQ!<9UIJ!Qi_m+4& zDKV2}C%U@X0`9N<0z&z1q*D5M=@A6+Pj`g3 z0eMP5iqp10n>yGLFHXFJpHN7tIIF&X?<}Qs3HK zv(1ghX5&Gd<9kRm=DZ@}DCwLSvP!te(fMWZ>#gSIw$tux-fw+j8$VrN-IjR>kUiIj zJPzEkVGzX}U!J{$Lb##pA7Xvrr0h5x@*l^?B=g?$t`}iV65Hk1p--h#yU^}5I$LdL zWqq}&9>2_Zm}h0a7pp`Vc0EwV5CoJu)EPD~0mTmp+UwdzXMO!EsrN!5bIlVP)mdp^ z4J57C=M9(d#nRk=rba#Lh0;JfN)s-6G62m%4a46@S;}cC!ysj#arq4tz)a04P42}* zUr6dn1A{yNKCGS!O)B@zp3ewBhE4GXbeINn)vmh5+fa8cii~v`tq0eiU zhPk=|zeyJ~Ohya%swA?r)0#xNlD#m+Y6o7(2E2x~pWql&`Bms- z4k{KLCJEeP*Jigi&>whHQeV|k;Ir)iW(#nY-fMnnzJ&N H1mKUB0gEIP0z$Hc$VS*&B4vTi1}z#uPJ6oTnW$%a=I?8HfB?(99$g4m5+Gn1aq})@T6A-yF^`3ycJQZWN6SP)Iq=l58|A1RN`_ zWuYQ|3_~STFfj+k-xpk)tyKdiAu%wZnlq5uJSF8OmJIGep~ME9B_;+fQx1{9)rgJe}m7 z@i^rHV|Lg6d*WFel?;NP)-+f#*~-UdZ3S*39o}mHvx{v1 zP*p`!R&if)+yqs(p>=C)wQ5;o%jO?=&D_RVS$mvHRtWyY{;czHHMi+E9N((=2.4; internal change for ``Transport.perform_request`` + # that just returns the body + data = result + + took = data.get("took") + if took: + span.set_metric(metadata.TOOK, int(took)) + except Exception: + pass + + if status: + span.set_tag(http.STATUS_CODE, status) + + return result + + return _perform_request diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/elasticsearch/quantize.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/elasticsearch/quantize.py new file mode 100644 index 000000000..5b526a3fa --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/elasticsearch/quantize.py @@ -0,0 +1,35 @@ +import re + +from ...ext import elasticsearch as metadata + + +# Replace any ID +ID_REGEXP = re.compile(r"/([0-9]+)([/\?]|$)") +ID_PLACEHOLDER = r"/?\2" + +# Remove digits from potential timestamped indexes (should be an option). +# For now, let's say 2+ digits +INDEX_REGEXP = re.compile(r"[0-9]{2,}") +INDEX_PLACEHOLDER = r"?" + + +def quantize(span): + """Quantize an elasticsearch span + + We want to extract a meaningful `resource` from the request. + We do it based on the method + url, with some cleanup applied to the URL. + + The URL might a ID, but also it is common to have timestamped indexes. + While the first is easy to catch, the second should probably be configurable. + + All of this should probably be done in the Agent. Later. + """ + url = span.get_tag(metadata.URL) + method = span.get_tag(metadata.METHOD) + + quantized_url = ID_REGEXP.sub(ID_PLACEHOLDER, url) + quantized_url = INDEX_REGEXP.sub(INDEX_PLACEHOLDER, quantized_url) + + span.resource = "{method} {url}".format(method=method, url=quantized_url) + + return span diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/falcon/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/falcon/__init__.py new file mode 100644 index 000000000..d67e1f715 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/falcon/__init__.py @@ -0,0 +1,57 @@ +""" +To trace the falcon web framework, install the trace middleware:: + + import falcon + from ddtrace import tracer + from ddtrace.contrib.falcon import TraceMiddleware + + mw = TraceMiddleware(tracer, 'my-falcon-app') + falcon.API(middleware=[mw]) + +You can also use the autopatching functionality:: + + import falcon + from ddtrace import tracer, patch + + patch(falcon=True) + + app = falcon.API() + +To disable distributed tracing when using autopatching, set the +``DATADOG_FALCON_DISTRIBUTED_TRACING`` environment variable to ``False``. + +**Supported span hooks** + +The following is a list of available tracer hooks that can be used to intercept +and modify spans created by this integration. + +- ``request`` + - Called before the response has been finished + - ``def on_falcon_request(span, request, response)`` + + +Example:: + + import falcon + from ddtrace import config, patch_all + patch_all() + + app = falcon.API() + + @config.falcon.hooks.on('request') + def on_falcon_request(span, request, response): + span.set_tag('my.custom', 'tag') + +:ref:`Headers tracing ` is supported for this integration. +""" +from ...utils.importlib import require_modules + + +required_modules = ["falcon"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .middleware import TraceMiddleware + from .patch import patch + + __all__ = ["TraceMiddleware", "patch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/falcon/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/falcon/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d2fc6913c7d82bb004797f39ee8db02cc5fa9983 GIT binary patch literal 1631 zcmbVM-EP}96qanqPK0#XBk(S^VN2^PU* z0}OkC-uH5^liOYO6?)Y}QE-ZG7|;>0%-?gqbH4AKXLs%d9*ldh|0I9yc;4T3@!{Bm z#Y6b%3;0kk_NY%AnV&b}hVRkl!jGG*!J5DOfAr%PYv~DX(Vc83Z^vyoZ_#$v$vbfe z<{i4bXvDied0%@!dKcc$9^Je2dI5RW^1NUwkx^vMkXbU6l58#oTC*8S70KCJsuu&q zg*GJ1svGWZ&MD2n_tC38fndr%)&3-yoV5S$vtXJPHXzCE?L zzTdiXj`B4+K_8uTo!tRC%r`@4V@QhPu;)BgL51TV&bqg_PkznUzx9IP7g?e?5r|}3 zqEfpwkkZJ4nE8^61xiaXH(UymakKe9;SNw`%fYXvU5D{xs!9eJZ2kZ)#O3xYsIU*z zP_D@=WA@)Bs5FdLbXj2QB@+PIPCo=ZK$;oLRgff;@pL?S`UF27e|P%y98bwUO6-pCCOvR3QLkO2>SiYvak$;e_cSnmQudZ z{XT%SvXNOP*A@uZh@cF>BAFtxB0O`}ER)JT0%*ikIiE3`aR^9)#0*n&R+xYYigHPL zx~Uv#G*^sRx3dkr1pD^tLJ>;|02=~K#a@<7nXn^1J3a6f1dpyrUSywG0wDF2FKSI-Kw2TF zWMNm`|ImZg$wT+Pu9UE%A(WzfSYxa;|7p>TDh$@VjW-1SU_%yNsHSjUY9sSQC@t6l zOpUX19bcgg>ReC-+KPdfsKGGhJpPu zV>)g-F5dd}`A0<$Pj;%bDQIJ!f)?tX_x5f&7*jdNI6f#1&vfW|NycZY0XO2$YcF)` z<{;5rlz~l3bbU8Eo{9DIqkGT4 zPTq>k9B(!`i|TH<{jb|%F89vg!u8 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/falcon/__pycache__/middleware.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/falcon/__pycache__/middleware.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b24b8233e43ebbd88e36c7b37e73ec3ffd66a67 GIT binary patch literal 3181 zcmai0TW=f372eq!mp9$3#%++Y7q?~6kc#UXaI3meZKb!EH6pq}wk-y$osqQEl1tAH zWr7$=%|f~N zkH1I%Ua+kHQ0IDa(Rmk7{x=w536@$R)pp8;c4#wdJE=2tL)Wxjw7t+r+Y^3T83tj1 zc0~kfbyy2)rd>t59@b5}mNtgXu*s}nTcR!+FD%iJ3ujKaD4Js7g%vJ|MX`i>SuBec z)GLDRSnbtU@W$%3S?R~wXfN4EtGY9eM*Wkq%+YZVv>v~*F$tZzR#7bZL?>zf%9)Mb z+Ujk+|55+94|n*^*5fDdZ}Shf`rG{X+aF=U^3Id39)G;Owe$3Y?ML&$YNDmWGt6W9 z`1L}2zKbW{fRR?ngcaI2jw9R`EOZD8xb6}Rs6EjTRZ+tmU)VTIvs~jNuc7-sGT-7)>dE1>K&>dcZ(ixwW0q4mm(VUZOc>rVCKJ!_?rf=N9uyZ*z zCd@92hXyS(82%Lv4N439g{4`+2KE_yWSy}ycIs$n;1<@v)Bd0WY@L@5v#37^1dUg9 zZBQ59OXt)r-1CNRUNF3;)&i{zQqqGm?UrX3wQ+JEh&(DA{4j zj?9J4%nCLstDF0BB#+1H!PjYHB9v?S*7VLz?8YjKWu9{-pG{=0JDX`1M``|`bG1R+ z4r|Fs^O^k0#raHrAS2?#bYXUXo%wj%Hbe@mIYi1eRN8N|aCwqTzMp2hQ99pr$M8kt-68*B|Ux0%b_|N36dS+`wg zMpth%@FO(9XX~6@Sf^g$+IG|bia zXoMomHKH!P#$vVyt(fVZ)d&VQZT;^f&z5}d%ygf0Zgb{;q4C8t7xutDN94RfJh)|@ z`X-_VsxO>VADU+a*Wdt*&V5}O1cguPMz4ASArd;ec3!_gYQ*YFQ8DrO;{v!H2CC4F zqEdKg_NhNw!I+1!k6(}J=3t@l^dcgK4?mWL{}Snx`h%78Re_a=_4WnEu79r`^gdxN zMS-Jq?rz+j`nVd@@3rl+jxehyIbEM=QIAt$+0hrZb3&AV>KVvAEZ+P_7~! za9-9pA7)~L*ul#N=g%flI(ws5F^--vO^*NHUhOEw5zJ?ryZ~{@_zuIg}wdk*oaNvkLJ$Pi8(b5O9MJ}~w z)Myb`TYD-Snm6;nLNDV(6S7;NQmusoI+-bEbQiB%Ix_?N$&nnj=#QY8S2{o4*ib)) zXFc2v`5&6VeYFmw=Ja0@gVa4t{}Y~^fV5nY0ptjn8UF(64yG8?3B4KWKpr`sqNhQh z;${bN(^0>`l(NcsoJL5oe0~ijQ#UR_{Qwj9@LXO1_1mlJE~>CH|KDWv6V~Uwjy%?i zd{Hi$J20hX#8y_3sR89k=O($CG#4%+g-eAD*NL;?IGX2HKieHZxrSc`HFIX;&NUaW e*jWafGkQND{^jd10n~%v%(8~F^t-?f>Yo7(YxtG` literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/falcon/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/falcon/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3ac2a9a7d0fe3cc1cb8f29b8f745acf37a68ed26 GIT binary patch literal 1352 zcmZ8hOK%%D5GMES)k=2kB<)eRLWgLbvsx;U+F(7rfjn{KPldM;s)9!9l)Ngh_~a3&Olp zL`g)62n9Lsu4vK|*7Y`Y#E$Sq03C>+3%x6QMPd7j-e`|IaPK#g+=E@%!*v(v2^rkK z#rVi*KwB@Z%5gT)NJ*XwiO9W_yjHh1UeS8qoK;nh$e#$sL^(s)t+{Ljn+e%uRe4L- zSGP~Ki61k7TwHP~T5rsAbkOcW)+m{sO_czq{bc3j^dnjXG3L`;4XBLJM2ho2!^PbT zoXu+#iYN*ctd%Wyz&;_$GJ9u*Tq+yf3s*i-&NVU#zVdF?+Teo7R~zltTC2Yugo~4~ zkK>rD^lg6~=J4?NsIRJiR;KwBL?0v9K@T^l|9hPI{A}LeJi%;}9s{m8R1;Qf!!Jjt z16%4>NKMgNbPz3~!@3?amX(=eY+&DFHQl*`$Hzxsj(tNtS4v9#aqBli>0R4i9ISv-(k1Hj3x@quHW9n9mCl z0~kl2*A0vDn#I`gGOXvVYtYviLDNG)+=x0>L<8ExDIcOH)zA4fJOp~0a(IqlXB?_}oMkB%H9lELC3Uk)(=Ff#jG_rPSU>W^j&jgnE8(K|3#%t2ssD?99LM7M9 z?@m>gH^Xry3$7Ze8IwMWWOK0TSQ+Spue8uk+YbBG%Z$lgecxdmpmiWil}Uz)=1%0M pesuV4@T{m{nv3ssi8p-|0p?M^!blp~q3uu$e>?v;e>*|2{V)ABPyqk{ literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/falcon/middleware.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/falcon/middleware.py new file mode 100644 index 000000000..9c4539237 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/falcon/middleware.py @@ -0,0 +1,103 @@ +import sys + +from ddtrace import config +from ddtrace.ext import SpanTypes +from ddtrace.ext import http as httpx + +from .. import trace_utils +from ...constants import ANALYTICS_SAMPLE_RATE_KEY +from ...constants import SPAN_MEASURED_KEY +from ...internal.compat import iteritems + + +class TraceMiddleware(object): + def __init__(self, tracer, service="falcon", distributed_tracing=None): + # store tracing references + self.tracer = tracer + self.service = service + if distributed_tracing is not None: + config.falcon["distributed_tracing"] = distributed_tracing + + def process_request(self, req, resp): + # Falcon uppercases all header names. + headers = dict((k.lower(), v) for k, v in iteritems(req.headers)) + trace_utils.activate_distributed_headers(self.tracer, int_config=config.falcon, request_headers=headers) + + span = self.tracer.trace( + "falcon.request", + service=self.service, + span_type=SpanTypes.WEB, + ) + span.set_tag(SPAN_MEASURED_KEY) + + # set analytics sample rate with global config enabled + span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, config.falcon.get_analytics_sample_rate(use_global_config=True)) + + trace_utils.set_http_meta( + span, config.falcon, method=req.method, url=req.url, query=req.query_string, request_headers=req.headers + ) + + def process_resource(self, req, resp, resource, params): + span = self.tracer.current_span() + if not span: + return # unexpected + span.resource = "%s %s" % (req.method, _name(resource)) + + def process_response(self, req, resp, resource, req_succeeded=None): + # req_succeded is not a kwarg in the API, but we need that to support + # Falcon 1.0 that doesn't provide this argument + span = self.tracer.current_span() + if not span: + return # unexpected + + status = resp.status.partition(" ")[0] + + # FIXME[matt] falcon does not map errors or unmatched routes + # to proper status codes, so we we have to try to infer them + # here. See https://github.com/falconry/falcon/issues/606 + if resource is None: + status = "404" + span.resource = "%s 404" % req.method + span.set_tag(httpx.STATUS_CODE, status) + span.finish() + return + + err_type = sys.exc_info()[0] + if err_type is not None: + if req_succeeded is None: + # backward-compatibility with Falcon 1.0; any version + # greater than 1.0 has req_succeded in [True, False] + # TODO[manu]: drop the support at some point + status = _detect_and_set_status_error(err_type, span) + elif req_succeeded is False: + # Falcon 1.1+ provides that argument that is set to False + # if get an Exception (404 is still an exception) + status = _detect_and_set_status_error(err_type, span) + + trace_utils.set_http_meta(span, config.falcon, status_code=status, response_headers=resp._headers) + + # Emit span hook for this response + # DEV: Emit before closing so they can overwrite `span.resource` if they want + config.falcon.hooks.emit("request", span, req, resp) + + # Close the span + span.finish() + + +def _is_404(err_type): + return "HTTPNotFound" in err_type.__name__ + + +def _detect_and_set_status_error(err_type, span): + """Detect the HTTP status code from the current stacktrace and + set the traceback to the given Span + """ + if not _is_404(err_type): + span.set_traceback() + return "500" + elif _is_404(err_type): + return "404" + + +def _name(r): + return "%s.%s" % (r.__module__, r.__class__.__name__) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/falcon/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/falcon/patch.py new file mode 100644 index 000000000..32bf3e5c8 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/falcon/patch.py @@ -0,0 +1,46 @@ +import falcon + +from ddtrace import config +from ddtrace import tracer +from ddtrace.vendor import wrapt + +from ...utils.formats import asbool +from ...utils.formats import get_env +from ...utils.version import parse_version +from .middleware import TraceMiddleware + + +FALCON_VERSION = parse_version(falcon.__version__) + + +config._add( + "falcon", + dict( + distributed_tracing=asbool(get_env("falcon", "distributed_tracing", default=True)), + ), +) + + +def patch(): + """ + Patch falcon.API to include contrib.falcon.TraceMiddleware + by default + """ + if getattr(falcon, "_datadog_patch", False): + return + + setattr(falcon, "_datadog_patch", True) + if FALCON_VERSION >= (3, 0, 0): + wrapt.wrap_function_wrapper("falcon", "App.__init__", traced_init) + if FALCON_VERSION < (4, 0, 0): + wrapt.wrap_function_wrapper("falcon", "API.__init__", traced_init) + + +def traced_init(wrapped, instance, args, kwargs): + mw = kwargs.pop("middleware", []) + service = config._get_service(default="falcon") + + mw.insert(0, TraceMiddleware(tracer, service)) + kwargs["middleware"] = mw + + wrapped(*args, **kwargs) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/fastapi/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/fastapi/__init__.py new file mode 100644 index 000000000..4519454a6 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/fastapi/__init__.py @@ -0,0 +1,66 @@ +""" +The fastapi integration will trace requests to and from FastAPI. + +Enabling +~~~~~~~~ + +The fastapi integration is enabled automatically when using +:ref:`ddtrace-run` or :ref:`patch_all()`. + +Or use :ref:`patch()` to manually enable the integration:: + + from ddtrace import patch + from fastapi import FastAPI + + patch(fastapi=True) + app = FastAPI() + + +If using Python 3.6, the legacy ``AsyncioContextProvider`` will have to be +enabled before using the middleware:: + + from ddtrace.contrib.asyncio.provider import AsyncioContextProvider + from ddtrace import tracer # Or whichever tracer instance you plan to use + tracer.configure(context_provider=AsyncioContextProvider()) + + +Configuration +~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.fastapi['service_name'] + + The service name reported for your fastapi app. + + Can also be configured via the ``DD_SERVICE`` environment variable. + + Default: ``'fastapi'`` + +.. py:data:: ddtrace.config.fastapi['request_span_name'] + + The span name for a fastapi request. + + Default: ``'fastapi.request'`` + + +Example:: + + from ddtrace import config + + # Override service name + config.fastapi['service_name'] = 'custom-service-name' + + # Override request span name + config.fastapi['request_span_name'] = 'custom-request-span-name' + +""" +from ...utils.importlib import require_modules + + +required_modules = ["fastapi"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + from .patch import unpatch + + __all__ = ["patch", "unpatch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/fastapi/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/fastapi/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..00e6e41babaa32561f9494b63d8f8875eb89e01e GIT binary patch literal 1780 zcma)6&2HO95GM8O5Ki4kSfB?r9Nrb*az{P6Gk|nLE zY~&=N)*v?l!m6_pp8md~Dxd_THgh{MC1Z}#)V~9rYI-66vY?t=zP1|?zKF(v5O&)=wlkSYCNJ2C$3`Hboz<=<3uvWC2rWX2#DNHjaOsQXb@zB3{G!}5 zA+wWHI3X<(e*h&%{*tep_zMUjs(PHs083l$s_HJctG@cGzv=h87LKRC{2cw*v8><7 zSRHFyv z$b*<4F1?xA0#M&gRIN{kTns{~#?d6x zVXa`A#!76pyJ1~LMOZ|+5<4=jHKflcTFFpjd})z1J5iE=N+PY6vDL9n>9xZ_$?^Rh z-}(_w+M2T~c4=#NW=pnj!GpOC52StV%$aodt+{<;&#jP43z6a{K!u-mUS1x&ylmWD z=@+FOb^u9W0i6w0`o8OHqiXo4t5sb!V zCKc49*^|-H^WyyM@afry@Z_VSIt;&0ild|1$A>hMYIHiP(&}+qAdXq~csi?&E-rF4 zl9IrDZ624A)0gNC)8RrMK@m(4j7&} zw8eH>Xq7-@=(1p-31;XwPE}Toiy14>Kve|_@D6w5huo_qqB-m|-hyqi@eN8vtQsCc zQaA3mNO6>Qer{^8n6~h+X5FAC!?Z~CeO#z1XN&dNJwoLz)MHv$9slB!W)iWua$rDN zA|_-zR}QnZBf0EoPP#$BJ!v*V3^tNw5}LvspU!q~I|4f;OJf5>JsTWj!+iGD7eQ!3 z3q#5edoI!%7Nqm)h1J;DWfbU2YD6?J`E|^1K0te!8Mk{34shlFGYfE6Teq3jtC?Y@ zlo|F9Rvz;-x7(su-o~$v8-E$G3-92Hi5A{P4I`b1-fEh{Ytwvf?B1kUuou5wjoya$ zab>t~P8Qp@_^oh@$RfP9H4I_I{niTF1_`1%NrRPdsI33>!fu01X{`biis^j3PZv!W z5O<-7vS4u&0yPxgy6x^nr&%4eS6~euZo+%yv3u(=RfR0UxM(2e0dbHZq&qJR8wU>- zJdC7lxJ+YhD8H!o0l5W4V_tcQhPK8|m5X5T%3^1t}H@A{ta F{|$Oszi|Kn literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/fastapi/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/fastapi/patch.py new file mode 100644 index 000000000..99aca8a43 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/fastapi/patch.py @@ -0,0 +1,52 @@ +import fastapi +from fastapi.middleware import Middleware + +from ddtrace import config +from ddtrace.contrib.asgi.middleware import TraceMiddleware +from ddtrace.contrib.starlette.patch import get_resource +from ddtrace.internal.logger import get_logger +from ddtrace.utils.wrappers import unwrap as _u +from ddtrace.vendor.wrapt import wrap_function_wrapper as _w + + +log = get_logger(__name__) + +config._add( + "fastapi", + dict( + _default_service="fastapi", + request_span_name="fastapi.request", + distributed_tracing=True, + aggregate_resources=True, + ), +) + + +def span_modifier(span, scope): + resource = get_resource(scope) + if config.fastapi["aggregate_resources"] and resource: + span.resource = "{} {}".format(scope["method"], resource) + + +def traced_init(wrapped, instance, args, kwargs): + mw = kwargs.pop("middleware", []) + mw.insert(0, Middleware(TraceMiddleware, integration_config=config.fastapi, span_modifier=span_modifier)) + kwargs.update({"middleware": mw}) + wrapped(*args, **kwargs) + + +def patch(): + if getattr(fastapi, "_datadog_patch", False): + return + + setattr(fastapi, "_datadog_patch", True) + _w("fastapi.applications", "FastAPI.__init__", traced_init) + + +def unpatch(): + if not getattr(fastapi, "_datadog_patch", False): + return + + setattr(fastapi, "_datadog_patch", False) + + _u(fastapi.applications.FastAPI, "__init__") diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/__init__.py new file mode 100644 index 000000000..597e037ae --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/__init__.py @@ -0,0 +1,100 @@ +""" +The Flask__ integration will add tracing to all requests to your Flask application. + +This integration will track the entire Flask lifecycle including user-defined endpoints, hooks, +signals, and template rendering. + +To configure tracing manually:: + + from ddtrace import patch_all + patch_all() + + from flask import Flask + + app = Flask(__name__) + + + @app.route('/') + def index(): + return 'hello world' + + + if __name__ == '__main__': + app.run() + + +You may also enable Flask tracing automatically via ddtrace-run:: + + ddtrace-run python app.py + + +Configuration +~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.flask['distributed_tracing_enabled'] + + Whether to parse distributed tracing headers from requests received by your Flask app. + + Default: ``True`` + +.. py:data:: ddtrace.config.flask['service_name'] + + The service name reported for your Flask app. + + Can also be configured via the ``DD_SERVICE`` environment variable. + + Default: ``'flask'`` + +.. py:data:: ddtrace.config.flask['collect_view_args'] + + Whether to add request tags for view function argument values. + + Default: ``True`` + +.. py:data:: ddtrace.config.flask['template_default_name'] + + The default template name to use when one does not exist. + + Default: ```` + +.. py:data:: ddtrace.config.flask['trace_signals'] + + Whether to trace Flask signals (``before_request``, ``after_request``, etc). + + Default: ``True`` + + +Example:: + + from ddtrace import config + + # Enable distributed tracing + config.flask['distributed_tracing_enabled'] = True + + # Override service name + config.flask['service_name'] = 'custom-service-name' + + # Report 401, and 403 responses as errors + config.http_server.error_statuses = '401,403' + +.. __: http://flask.pocoo.org/ + +:ref:`All HTTP tags ` are supported for this integration. + +""" + +from ...utils.importlib import require_modules + + +required_modules = ["flask"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + # DEV: We do this so we can `@mock.patch('ddtrace.contrib.flask._patch.')` in tests + from . import patch as _patch + from .middleware import TraceMiddleware + + patch = _patch.patch + unpatch = _patch.unpatch + + __all__ = ["TraceMiddleware", "patch", "unpatch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8dbd92a09905f396b11641030e4fb51490677b64 GIT binary patch literal 2367 zcmb7GO-~~?7@mX>V3rkCk3Cu1Lx@B&(50&q0on?v+C#tCY`2}N@;GBBF?j5$?MVoS zUit&7{(;{6m$>${f1#(o_Dn*SaM-nkqp{yFKhOKY>!(k{0M?7Y|017k1;M}WV|l?@ z@Dlz^0viqDfYxYT)UtY9uLX2%REyVGovr;*`}6a-!5Ze0Ht4!o&zkWD+&5@bY-U^W zRxMx~SIxKydl-jwgKpBT(OUfEP4HRpLvR`V6wvTh(0)RC8$l5E#|(Wbh`GiXacS8| z6U&uEQ!WG|lp?E1%H;@Ig$NvJ_FKV>HST++3SBBAlIMb_UbP#7QEpbPIZM~bfiTuR4~v;MM0gj!Z6*TY{(^}AWd@xv6($IR_fa9g@%tL5%5hU1jw>n5X&Gm zNtp(97lA^ll0!Z!z|Z^%8Ic9NVb<$~A%Zp3DnpdIYjB?BN?Vi@n~pI^d5Oi>ZhJ{( z=QF%wPUdKIbRp*`RRFmol+WxKlEZn)XW-(j!q^iZ{=U4zu-gUs9wnCadUM0w zl9jH9@Rt_l#%ewQR4A@k!eyki^{WT&&6q*PXqRp|(T1TVmBl;8i~#edBy^FXnx%|S zAb|$62dN4P0!W{;At{9Ip(N?+f+fj&g$$WF;VJXKSnLktFBm_|?Vbk6)i%Bnfag;abTI$_7n{=8nmgC$v1It@nIIs)S&v z#S_k^nCOvNh1(T>g(b3N1n9zJoIDy9GWAUY6pJb<0bOAwrVoI1Ud0%S!m;uY-|G0j z+C9*a70@csbj&1D5|TqPgQT*E-9Too#B-dnOzGL_dxGQ}1Fo9QDnMo5C@ZQGLc2*a zU;q@u6?93m2XvC5WqSFD*|fb9-W;y*;)Xy^WAh4ptWTA7RQ8r<=%TFdM{2|Ol@BO7 zXkadkx#907OlwZx&F@ESzFQSwq?Hy1noFm;=s3EdkGT%_3|;#AK!^JWWeY#te-5c; zawQG$M+^d|N}FXrV{3Elj4|Eyx7b)>fqdhfIZI#!96-~-xQCp0FN%CkbaRy|)m3^F zg<(&#VK4atCfQg0{`X~KA3Ny|uzTuP#HU$rQ9Rzd(w!4i&dZ};Q+jkonQ2+^6` z=0~$x#-eI)EPf#iU9&Gqi7@7pTa00l+-)A0;GH_q%*U`bUT?28>tU@?->z-fo3&>B Ee`6l{OaK4? literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/__pycache__/helpers.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/__pycache__/helpers.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..03ec2c66e9c1a5312e6309710365949d9f6191f6 GIT binary patch literal 1516 zcmZWp&2HQ_5ay4xE8Dw?lO|~j6oJ}9Q5~{!+Qxu^z%WptEqXB0x?t48WNoDGtAxZ>S&p{Z#_8`TK9ljzh?w zIQge=nC!sN-+~fE(40hQSxzS`VibK2K94x~j&O5l;zlm`9nrlYQAc=U1#DOJ#Hv`k zpphr&9trwyAs*QcXw6?{1(;4cEr!|XErVs4t{Ii2GMEW%Iz{3Tc@WJ z846+RaBC5p$__${D?GFb>Opy0--Q(8Wfq8b1DvQt+0lC@#<c%ey06o zo@6<$_`@kM0Tk@p9E()$r_gV=;)2(K@g5Al3CfUbT9Jwxc23^#GpBOYhm~{9-w^!? zth0*028dUra_}phpSu8=0&NO2tKE2*6~d++b(gRLx9&E$MeVgfY9~=6U3ZR-t>pmy zhSm-yYB^O{%l4dlfB(_GSQg2~37EPMhZUyS z1ipu!jehTcF>p`Y3h`do-kM@stsrT*YsqV#!6mt*L)H*@PS04yEQK$>mW5Q%kwBi# zCM9rTdkX1DjHOXGkE3N4pyxgU^7pN7J|9@~X|SInSFNCron?930SBHSJjg3z*fp<+ zamH>1{9Qp~{lb5JOpz;IIj`wYgpuduJMumGgX~%RGyF^N;qC@3>T0RxSt{!lT_#0r zPD>edqE3UPb{mU#M{8MO;^o&0tD&%s0#{*2s~)O8synFeg1TX~QzR3q9^wizX(_O@ zd<3(m=**{!Cr!EHW&ZyqHK3N3fUCzC#D;sv?fY%uy8o{*Pw)zaNI0YGj`{#R?AjHJ z8~&5ci?=;&^Qno#L~SG63+<5xxJ7@PLhZenOvNmh+xW~i=I*f`?Xe#BJg@&Zw~}k$ literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/__pycache__/middleware.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/__pycache__/middleware.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..50e0bcc8c5c76c5faf6aa3d00c4a2857389cfedd GIT binary patch literal 6438 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 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/helpers.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/helpers.py new file mode 100644 index 000000000..78ab99112 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/helpers.py @@ -0,0 +1,38 @@ +import flask + +from ddtrace import Pin +from ddtrace import config + +from .. import trace_utils + + +def get_current_app(): + """Helper to get the flask.app.Flask from the current app context""" + appctx = flask._app_ctx_stack.top + if appctx: + return appctx.app + return None + + +def with_instance_pin(func): + """Helper to wrap a function wrapper and ensure an enabled pin is available for the `instance`""" + + def wrapper(wrapped, instance, args, kwargs): + pin = Pin._find(wrapped, instance, get_current_app()) + if not pin or not pin.enabled(): + return wrapped(*args, **kwargs) + + return func(pin, wrapped, instance, args, kwargs) + + return wrapper + + +def simple_tracer(name, span_type=None): + """Generate a simple tracer that wraps the function call with `with tracer.trace()`""" + + @with_instance_pin + def wrapper(pin, wrapped, instance, args, kwargs): + with pin.tracer.trace(name, service=trace_utils.int_service(pin, config.flask, pin), span_type=span_type): + return wrapped(*args, **kwargs) + + return wrapper diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/middleware.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/middleware.py new file mode 100644 index 000000000..c29da4ce8 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/middleware.py @@ -0,0 +1,233 @@ +from flask import g +from flask import request +from flask import signals +import flask.templating + +from ddtrace import config +from ddtrace.constants import ERROR_MSG +from ddtrace.constants import ERROR_TYPE + +from .. import trace_utils +from ...ext import SpanTypes +from ...ext import http +from ...internal import compat +from ...internal.logger import get_logger +from ...utils.cache import cached +from ...utils.deprecation import deprecated + + +log = get_logger(__name__) + + +SPAN_NAME = "flask.request" + + +@cached() +def _normalize_resource(resource): + return compat.to_unicode(resource).lower() + + +class TraceMiddleware(object): + @deprecated(message="Use patching instead (see the docs).", version="1.0.0") + def __init__(self, app, tracer, service="flask", use_signals=True, distributed_tracing=None): + self.app = app + log.debug("flask: initializing trace middleware") + + # Attach settings to the inner application middleware. This is required if double + # instrumentation happens (i.e. `ddtrace-run` with `TraceMiddleware`). In that + # case, `ddtrace-run` instruments the application, but then users code is unable + # to update settings such as `distributed_tracing` flag. This step can be removed + # when the `Config` object is used + self.app._tracer = tracer + self.app._service = service + self.app._use_distributed_tracing = distributed_tracing + self.use_signals = use_signals + + # safe-guard to avoid double instrumentation + if getattr(app, "__dd_instrumentation", False): + return + setattr(app, "__dd_instrumentation", True) + + # Install hooks which time requests. + self.app.before_request(self._before_request) + self.app.after_request(self._after_request) + self.app.teardown_request(self._teardown_request) + + # Add exception handling signals. This will annotate exceptions that + # are caught and handled in custom user code. + # See https://github.com/DataDog/dd-trace-py/issues/390 + if use_signals and not signals.signals_available: + log.debug(_blinker_not_installed_msg) + self.use_signals = use_signals and signals.signals_available + timing_signals = { + "got_request_exception": self._request_exception, + } + self._receivers = [] + if self.use_signals and _signals_exist(timing_signals): + self._connect(timing_signals) + + _patch_render(tracer) + + def _connect(self, signal_to_handler): + connected = True + for name, handler in signal_to_handler.items(): + s = getattr(signals, name, None) + if s is None: + connected = False + log.warning("trying to instrument missing signal %s", name) + continue + # we should connect to the signal without using weak references + # otherwise they will be garbage collected and our handlers + # will be disconnected after the first call; for more details check: + # https://github.com/jek/blinker/blob/207446f2d97/blinker/base.py#L106-L108 + s.connect(handler, sender=self.app, weak=False) + self._receivers.append(handler) + return connected + + def _before_request(self): + """Starts tracing the current request and stores it in the global + request object. + """ + self._start_span() + + def _after_request(self, response): + """Runs after the server can process a response.""" + try: + self._process_response(response) + except Exception: + log.debug("flask: error tracing response", exc_info=True) + return response + + def _teardown_request(self, exception): + """Runs at the end of a request. If there's an unhandled exception, it + will be passed in. + """ + # when we teardown the span, ensure we have a clean slate. + span = g.__dict__.pop("flask_datadog_span", None) + if span is None: + return + + try: + self._finish_span(span, exception=exception) + except Exception: + log.debug("flask: error finishing span", exc_info=True) + + def _start_span(self): + trace_utils.activate_distributed_headers( + self.app._tracer, + int_config=config.flask, + request_headers=request.headers, + override=self.app._use_distributed_tracing, + ) + + try: + g.flask_datadog_span = self.app._tracer.trace( + SPAN_NAME, + service=self.app._service, + span_type=SpanTypes.WEB, + ) + except Exception: + log.debug("flask: error tracing request", exc_info=True) + + def _process_response(self, response): + span = getattr(g, "flask_datadog_span", None) + if not span: + return + + code = response.status_code if response else "" + trace_utils.set_http_meta(span, config.flask, status_code=code, response_headers=response.headers) + + def _request_exception(self, *args, **kwargs): + exception = kwargs.get("exception", None) + span = getattr(g, "flask_datadog_span", None) + if span and exception: + _set_error_on_span(span, exception) + + def _finish_span(self, span, exception=None): + if not span: + return + + code = span.get_tag(http.STATUS_CODE) or 0 + try: + code = int(code) + except Exception: + code = 0 + + if exception: + # if the request has already had a code set, don't override it. + code = code or 500 + _set_error_on_span(span, exception) + + # the endpoint that matched the request is None if an exception + # happened so we fallback to a common resource + span.error = 0 if code < 500 else 1 + + # the request isn't guaranteed to exist here, so only use it carefully. + method = "" + endpoint = "" + url = "" + query = "" + request_headers = "" + + if request: + method = request.method + endpoint = request.endpoint or code + url = request.base_url or "" + query = request.query_string.decode() if hasattr(request.query_string, "decode") else request.query_string + request_headers = request.headers + + # Let users specify their own resource in middleware if they so desire. + # See case https://github.com/DataDog/dd-trace-py/issues/353 + if span.resource == SPAN_NAME: + resource = endpoint or code + span.resource = _normalize_resource(resource) + + trace_utils.set_http_meta( + span, + config.flask, + method=method, + url=compat.to_unicode(url), + status_code=code, + query=query, + request_headers=request_headers, + ) + span.finish() + + +def _set_error_on_span(span, exception): + # The 3 next lines might not be strictly required, since `set_traceback` + # also get the exception from the sys.exc_info (and fill the error meta). + # Since we aren't sure it always work/for insuring no BC break, keep + # these lines which get overridden anyway. + span.set_tag(ERROR_TYPE, type(exception)) + span.set_tag(ERROR_MSG, exception) + # The provided `exception` object doesn't have a stack trace attached, + # so attach the stack trace with `set_traceback`. + span.set_traceback() + + +def _patch_render(tracer): + """patch flask's render template methods with the given tracer.""" + # fall back to patching global method + _render = flask.templating._render + + # If the method has already been patched and we're patching again then + # we have to patch again with the new tracer reference. + if hasattr(_render, "__dd_orig"): + _render = getattr(_render, "__dd_orig") + + def _traced_render(template, context, app): + with tracer.trace("flask.template", span_type=SpanTypes.TEMPLATE) as span: + span._set_str_tag("flask.template", compat.maybe_stringify(template.name) or "string") + return _render(template, context, app) + + setattr(_traced_render, "__dd_orig", _render) + flask.templating._render = _traced_render + + +def _signals_exist(names): + """Return true if all of the given signals exist in this version of flask.""" + return all(getattr(signals, n, False) for n in names) + + +_blinker_not_installed_msg = "please install blinker to use flask signals. " "http://flask.pocoo.org/docs/0.11/signals/" diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/patch.py new file mode 100644 index 000000000..a510a8567 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/patch.py @@ -0,0 +1,520 @@ +import flask +import werkzeug + +from ddtrace import Pin +from ddtrace import config +from ddtrace.vendor.wrapt import wrap_function_wrapper as _w + +from .. import trace_utils +from ...constants import ANALYTICS_SAMPLE_RATE_KEY +from ...constants import SPAN_MEASURED_KEY +from ...ext import SpanTypes +from ...internal.compat import maybe_stringify +from ...internal.logger import get_logger +from ...utils import get_argument_value +from ...utils.version import parse_version +from ...utils.wrappers import unwrap as _u +from .helpers import get_current_app +from .helpers import simple_tracer +from .helpers import with_instance_pin +from .wrappers import wrap_function +from .wrappers import wrap_signal + + +log = get_logger(__name__) + +FLASK_ENDPOINT = "flask.endpoint" +FLASK_VIEW_ARGS = "flask.view_args" +FLASK_URL_RULE = "flask.url_rule" +FLASK_VERSION = "flask.version" + +# Configure default configuration +config._add( + "flask", + dict( + # Flask service configuration + _default_service="flask", + app="flask", + collect_view_args=True, + distributed_tracing_enabled=True, + template_default_name="", + trace_signals=True, + ), +) + + +# Extract flask version into a tuple e.g. (0, 12, 1) or (1, 0, 2) +# DEV: This makes it so we can do `if flask_version >= (0, 12, 0):` +# DEV: Example tests: +# (0, 10, 0) > (0, 10) +# (0, 10, 0) >= (0, 10, 0) +# (0, 10, 1) >= (0, 10) +# (0, 11, 1) >= (0, 10) +# (0, 11, 1) >= (0, 10, 2) +# (1, 0, 0) >= (0, 10) +# (0, 9) == (0, 9) +# (0, 9, 0) != (0, 9) +# (0, 8, 5) <= (0, 9) +flask_version_str = getattr(flask, "__version__", "0.0.0") +flask_version = parse_version(flask_version_str) + + +def patch(): + """ + Patch `flask` module for tracing + """ + # Check to see if we have patched Flask yet or not + if getattr(flask, "_datadog_patch", False): + return + setattr(flask, "_datadog_patch", True) + + Pin().onto(flask.Flask) + + # flask.app.Flask methods that have custom tracing (add metadata, wrap functions, etc) + _w("flask", "Flask.wsgi_app", traced_wsgi_app) + _w("flask", "Flask.dispatch_request", request_tracer("dispatch_request")) + _w("flask", "Flask.preprocess_request", request_tracer("preprocess_request")) + _w("flask", "Flask.add_url_rule", traced_add_url_rule) + _w("flask", "Flask.endpoint", traced_endpoint) + if flask_version >= (2, 0, 0): + _w("flask", "Flask.register_error_handler", traced_register_error_handler) + else: + _w("flask", "Flask._register_error_handler", traced__register_error_handler) + + # flask.blueprints.Blueprint methods that have custom tracing (add metadata, wrap functions, etc) + _w("flask", "Blueprint.register", traced_blueprint_register) + _w("flask", "Blueprint.add_url_rule", traced_blueprint_add_url_rule) + + # flask.app.Flask traced hook decorators + flask_hooks = [ + "before_request", + "before_first_request", + "after_request", + "teardown_request", + "teardown_appcontext", + ] + for hook in flask_hooks: + _w("flask", "Flask.{}".format(hook), traced_flask_hook) + _w("flask", "after_this_request", traced_flask_hook) + + # flask.app.Flask traced methods + flask_app_traces = [ + "process_response", + "handle_exception", + "handle_http_exception", + "handle_user_exception", + "try_trigger_before_first_request_functions", + "do_teardown_request", + "do_teardown_appcontext", + "send_static_file", + ] + for name in flask_app_traces: + _w("flask", "Flask.{}".format(name), simple_tracer("flask.{}".format(name))) + + # flask static file helpers + _w("flask", "send_file", simple_tracer("flask.send_file")) + + # flask.json.jsonify + _w("flask", "jsonify", traced_jsonify) + + # flask.templating traced functions + _w("flask.templating", "_render", traced_render) + _w("flask", "render_template", traced_render_template) + _w("flask", "render_template_string", traced_render_template_string) + + # flask.blueprints.Blueprint traced hook decorators + bp_hooks = [ + "after_app_request", + "after_request", + "before_app_first_request", + "before_app_request", + "before_request", + "teardown_request", + "teardown_app_request", + ] + for hook in bp_hooks: + _w("flask", "Blueprint.{}".format(hook), traced_flask_hook) + + # flask.signals signals + if config.flask["trace_signals"]: + signals = [ + "template_rendered", + "request_started", + "request_finished", + "request_tearing_down", + "got_request_exception", + "appcontext_tearing_down", + ] + # These were added in 0.11.0 + if flask_version >= (0, 11): + signals.append("before_render_template") + + # These were added in 0.10.0 + if flask_version >= (0, 10): + signals.append("appcontext_pushed") + signals.append("appcontext_popped") + signals.append("message_flashed") + + for signal in signals: + module = "flask" + + # v0.9 missed importing `appcontext_tearing_down` in `flask/__init__.py` + # https://github.com/pallets/flask/blob/0.9/flask/__init__.py#L35-L37 + # https://github.com/pallets/flask/blob/0.9/flask/signals.py#L52 + # DEV: Version 0.9 doesn't have a patch version + if flask_version <= (0, 9) and signal == "appcontext_tearing_down": + module = "flask.signals" + + # DEV: Patch `receivers_for` instead of `connect` to ensure we don't mess with `disconnect` + _w(module, "{}.receivers_for".format(signal), traced_signal_receivers_for(signal)) + + +def unpatch(): + if not getattr(flask, "_datadog_patch", False): + return + setattr(flask, "_datadog_patch", False) + + props = [ + # Flask + "Flask.wsgi_app", + "Flask.dispatch_request", + "Flask.add_url_rule", + "Flask.endpoint", + "Flask.preprocess_request", + "Flask.process_response", + "Flask.handle_exception", + "Flask.handle_http_exception", + "Flask.handle_user_exception", + "Flask.try_trigger_before_first_request_functions", + "Flask.do_teardown_request", + "Flask.do_teardown_appcontext", + "Flask.send_static_file", + # Flask Hooks + "Flask.before_request", + "Flask.before_first_request", + "Flask.after_request", + "Flask.teardown_request", + "Flask.teardown_appcontext", + # Blueprint + "Blueprint.register", + "Blueprint.add_url_rule", + # Blueprint Hooks + "Blueprint.after_app_request", + "Blueprint.after_request", + "Blueprint.before_app_first_request", + "Blueprint.before_app_request", + "Blueprint.before_request", + "Blueprint.teardown_request", + "Blueprint.teardown_app_request", + # Signals + "template_rendered.receivers_for", + "request_started.receivers_for", + "request_finished.receivers_for", + "request_tearing_down.receivers_for", + "got_request_exception.receivers_for", + "appcontext_tearing_down.receivers_for", + # Top level props + "after_this_request", + "send_file", + "jsonify", + "render_template", + "render_template_string", + "templating._render", + ] + + if flask_version >= (2, 0, 0): + props.append("Flask.register_error_handler") + else: + props.append("Flask._register_error_handler") + + # These were added in 0.11.0 + if flask_version >= (0, 11): + props.append("before_render_template.receivers_for") + + # These were added in 0.10.0 + if flask_version >= (0, 10): + props.append("appcontext_pushed.receivers_for") + props.append("appcontext_popped.receivers_for") + props.append("message_flashed.receivers_for") + + for prop in props: + # Handle 'flask.request_started.receivers_for' + obj = flask + + # v0.9.0 missed importing `appcontext_tearing_down` in `flask/__init__.py` + # https://github.com/pallets/flask/blob/0.9/flask/__init__.py#L35-L37 + # https://github.com/pallets/flask/blob/0.9/flask/signals.py#L52 + # DEV: Version 0.9 doesn't have a patch version + if flask_version <= (0, 9) and prop == "appcontext_tearing_down.receivers_for": + obj = flask.signals + + if "." in prop: + attr, _, prop = prop.partition(".") + obj = getattr(obj, attr, object()) + _u(obj, prop) + + +# Wrap the `start_response` handler to extract response code +# DEV: We tried using `Flask.finalize_request`, which seemed to work, but gave us hell during tests +# DEV: The downside to using `start_response` is we do not have a `Flask.Response` object here, +# only `status_code`, and `headers` to work with +# On the bright side, this works in all versions of Flask (or any WSGI app actually) +def _wrap_start_response(func, span, request): + def traced_start_response(status_code, headers): + code, _, _ = status_code.partition(" ") + + # Override root span resource name to be ` 404` for 404 requests + # DEV: We do this because we want to make it easier to see all unknown requests together + # Also, we do this to reduce the cardinality on unknown urls + # DEV: If we have an endpoint or url rule tag, then we don't need to do this, + # we still want `GET /product/` grouped together, + # even if it is a 404 + if not span.get_tag(FLASK_ENDPOINT) and not span.get_tag(FLASK_URL_RULE): + span.resource = u" ".join((request.method, code)) + + trace_utils.set_http_meta(span, config.flask, status_code=code, response_headers=headers) + return func(status_code, headers) + + return traced_start_response + + +@with_instance_pin +def traced_wsgi_app(pin, wrapped, instance, args, kwargs): + """ + Wrapper for flask.app.Flask.wsgi_app + + This wrapper is the starting point for all requests. + """ + # DEV: This is safe before this is the args for a WSGI handler + # https://www.python.org/dev/peps/pep-3333/ + environ, start_response = args + + # Create a werkzeug request from the `environ` to make interacting with it easier + # DEV: This executes before a request context is created + request = werkzeug.Request(environ) + + # Configure distributed tracing + trace_utils.activate_distributed_headers(pin.tracer, int_config=config.flask, request_headers=request.headers) + + # Default resource is method and path: + # GET / + # POST /save + # We will override this below in `traced_dispatch_request` when we have a `RequestContext` and possibly a url rule + resource = u" ".join((request.method, request.path)) + with pin.tracer.trace( + "flask.request", + service=trace_utils.int_service(pin, config.flask), + resource=resource, + span_type=SpanTypes.WEB, + ) as span: + span.set_tag(SPAN_MEASURED_KEY) + # set analytics sample rate with global config enabled + sample_rate = config.flask.get_analytics_sample_rate(use_global_config=True) + if sample_rate is not None: + span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, sample_rate) + + span._set_str_tag(FLASK_VERSION, flask_version_str) + + start_response = _wrap_start_response(start_response, span, request) + + # DEV: We set response status code in `_wrap_start_response` + # DEV: Use `request.base_url` and not `request.url` to keep from leaking any query string parameters + trace_utils.set_http_meta( + span, + config.flask, + method=request.method, + url=request.base_url, + query=request.query_string, + request_headers=request.headers, + ) + + return wrapped(environ, start_response) + + +def traced_blueprint_register(wrapped, instance, args, kwargs): + """ + Wrapper for flask.blueprints.Blueprint.register + + This wrapper just ensures the blueprint has a pin, either set manually on + itself from the user or inherited from the application + """ + app = get_argument_value(args, kwargs, 0, "app") + # Check if this Blueprint has a pin, otherwise clone the one from the app onto it + pin = Pin.get_from(instance) + if not pin: + pin = Pin.get_from(app) + if pin: + pin.clone().onto(instance) + return wrapped(*args, **kwargs) + + +def traced_blueprint_add_url_rule(wrapped, instance, args, kwargs): + pin = Pin._find(wrapped, instance) + if not pin: + return wrapped(*args, **kwargs) + + def _wrap(rule, endpoint=None, view_func=None, **kwargs): + if view_func: + pin.clone().onto(view_func) + return wrapped(rule, endpoint=endpoint, view_func=view_func, **kwargs) + + return _wrap(*args, **kwargs) + + +def traced_add_url_rule(wrapped, instance, args, kwargs): + """Wrapper for flask.app.Flask.add_url_rule to wrap all views attached to this app""" + + def _wrap(rule, endpoint=None, view_func=None, **kwargs): + if view_func: + # TODO: `if hasattr(view_func, 'view_class')` then this was generated from a `flask.views.View` + # should we do something special with these views? Change the name/resource? Add tags? + view_func = wrap_function(instance, view_func, name=endpoint, resource=rule) + + return wrapped(rule, endpoint=endpoint, view_func=view_func, **kwargs) + + return _wrap(*args, **kwargs) + + +def traced_endpoint(wrapped, instance, args, kwargs): + """Wrapper for flask.app.Flask.endpoint to ensure all endpoints are wrapped""" + endpoint = kwargs.get("endpoint", args[0]) + + def _wrapper(func): + # DEV: `wrap_function` will call `func_name(func)` for us + return wrapped(endpoint)(wrap_function(instance, func, resource=endpoint)) + + return _wrapper + + +def traced_flask_hook(wrapped, instance, args, kwargs): + """Wrapper for hook functions (before_request, after_request, etc) are properly traced""" + func = get_argument_value(args, kwargs, 0, "f") + return wrapped(wrap_function(instance, func)) + + +def traced_render_template(wrapped, instance, args, kwargs): + """Wrapper for flask.templating.render_template""" + pin = Pin._find(wrapped, instance, get_current_app()) + if not pin or not pin.enabled(): + return wrapped(*args, **kwargs) + + with pin.tracer.trace("flask.render_template", span_type=SpanTypes.TEMPLATE): + return wrapped(*args, **kwargs) + + +def traced_render_template_string(wrapped, instance, args, kwargs): + """Wrapper for flask.templating.render_template_string""" + pin = Pin._find(wrapped, instance, get_current_app()) + if not pin or not pin.enabled(): + return wrapped(*args, **kwargs) + + with pin.tracer.trace("flask.render_template_string", span_type=SpanTypes.TEMPLATE): + return wrapped(*args, **kwargs) + + +def traced_render(wrapped, instance, args, kwargs): + """ + Wrapper for flask.templating._render + + This wrapper is used for setting template tags on the span. + + This method is called for render_template or render_template_string + """ + pin = Pin._find(wrapped, instance, get_current_app()) + span = pin.tracer.current_span() + + if not pin.enabled or not span: + return wrapped(*args, **kwargs) + + def _wrap(template, context, app): + name = maybe_stringify(getattr(template, "name", None) or config.flask.get("template_default_name")) + if name is not None: + span.resource = name + span._set_str_tag("flask.template_name", name) + return wrapped(*args, **kwargs) + + return _wrap(*args, **kwargs) + + +def traced__register_error_handler(wrapped, instance, args, kwargs): + """Wrapper to trace all functions registered with flask.app._register_error_handler""" + + def _wrap(key, code_or_exception, f): + return wrapped(key, code_or_exception, wrap_function(instance, f)) + + return _wrap(*args, **kwargs) + + +def traced_register_error_handler(wrapped, instance, args, kwargs): + """Wrapper to trace all functions registered with flask.app.register_error_handler""" + + def _wrap(code_or_exception, f): + return wrapped(code_or_exception, wrap_function(instance, f)) + + return _wrap(*args, **kwargs) + + +def request_tracer(name): + @with_instance_pin + def _traced_request(pin, wrapped, instance, args, kwargs): + """ + Wrapper to trace a Flask function while trying to extract endpoint information + (endpoint, url_rule, view_args, etc) + + This wrapper will add identifier tags to the current span from `flask.app.Flask.wsgi_app`. + """ + span = pin.tracer.current_span() + if not pin.enabled or not span: + return wrapped(*args, **kwargs) + + try: + request = flask._request_ctx_stack.top.request + + # DEV: This name will include the blueprint name as well (e.g. `bp.index`) + if not span.get_tag(FLASK_ENDPOINT) and request.endpoint: + span.resource = u" ".join((request.method, request.endpoint)) + span._set_str_tag(FLASK_ENDPOINT, request.endpoint) + + if not span.get_tag(FLASK_URL_RULE) and request.url_rule and request.url_rule.rule: + span.resource = u" ".join((request.method, request.url_rule.rule)) + span._set_str_tag(FLASK_URL_RULE, request.url_rule.rule) + + if not span.get_tag(FLASK_VIEW_ARGS) and request.view_args and config.flask.get("collect_view_args"): + for k, v in request.view_args.items(): + # DEV: Do not use `_set_str_tag` here since view args can be string/int/float/path/uuid/etc + # https://flask.palletsprojects.com/en/1.1.x/api/#url-route-registrations + span.set_tag(u".".join((FLASK_VIEW_ARGS, k)), v) + except Exception: + log.debug('failed to set tags for "flask.request" span', exc_info=True) + + with pin.tracer.trace( + ".".join(("flask", name)), service=trace_utils.int_service(pin, config.flask, pin) + ) as request_span: + request_span._ignore_exception(werkzeug.exceptions.NotFound) + return wrapped(*args, **kwargs) + + return _traced_request + + +def traced_signal_receivers_for(signal): + """Wrapper for flask.signals.{signal}.receivers_for to ensure all signal receivers are traced""" + + def outer(wrapped, instance, args, kwargs): + sender = get_argument_value(args, kwargs, 0, "sender") + # See if they gave us the flask.app.Flask as the sender + app = None + if isinstance(sender, flask.Flask): + app = sender + for receiver in wrapped(*args, **kwargs): + yield wrap_signal(app, signal, receiver) + + return outer + + +def traced_jsonify(wrapped, instance, args, kwargs): + pin = Pin._find(wrapped, instance, get_current_app()) + if not pin or not pin.enabled(): + return wrapped(*args, **kwargs) + + with pin.tracer.trace("flask.jsonify"): + return wrapped(*args, **kwargs) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/wrappers.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/wrappers.py new file mode 100644 index 000000000..47a9c1b83 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask/wrappers.py @@ -0,0 +1,48 @@ +from ddtrace import config +from ddtrace.vendor.wrapt import function_wrapper + +from .. import trace_utils +from ...pin import Pin +from ...utils.importlib import func_name +from .helpers import get_current_app + + +def wrap_function(instance, func, name=None, resource=None): + """ + Helper function to wrap common flask.app.Flask methods. + + This helper will first ensure that a Pin is available and enabled before tracing + """ + if not name: + name = func_name(func) + + @function_wrapper + def trace_func(wrapped, _instance, args, kwargs): + pin = Pin._find(wrapped, _instance, instance, get_current_app()) + if not pin or not pin.enabled(): + return wrapped(*args, **kwargs) + with pin.tracer.trace(name, service=trace_utils.int_service(pin, config.flask), resource=resource): + return wrapped(*args, **kwargs) + + return trace_func(func) + + +def wrap_signal(app, signal, func): + """ + Helper used to wrap signal handlers + + We will attempt to find the pin attached to the flask.app.Flask app + """ + name = func_name(func) + + @function_wrapper + def trace_func(wrapped, instance, args, kwargs): + pin = Pin._find(wrapped, instance, app, get_current_app()) + if not pin or not pin.enabled(): + return wrapped(*args, **kwargs) + + with pin.tracer.trace(name, service=trace_utils.int_service(pin, config.flask)) as span: + span.set_tag("flask.signal", signal) + return wrapped(*args, **kwargs) + + return trace_func(func) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask_cache/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask_cache/__init__.py new file mode 100644 index 000000000..c5c302e51 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask_cache/__init__.py @@ -0,0 +1,56 @@ +""" +The flask cache tracer will track any access to a cache backend. +You can use this tracer together with the Flask tracer middleware. + +The tracer supports both `Flask-Cache `_ +and `Flask-Caching `_. + +To install the tracer, ``from ddtrace import tracer`` needs to be added:: + + from ddtrace import tracer + from ddtrace.contrib.flask_cache import get_traced_cache + +and the tracer needs to be initialized:: + + Cache = get_traced_cache(tracer, service='my-flask-cache-app') + +Here is the end result, in a sample app:: + + from flask import Flask + + from ddtrace import tracer + from ddtrace.contrib.flask_cache import get_traced_cache + + app = Flask(__name__) + + # get the traced Cache class + Cache = get_traced_cache(tracer, service='my-flask-cache-app') + + # use the Cache as usual with your preferred CACHE_TYPE + cache = Cache(app, config={'CACHE_TYPE': 'simple'}) + + def counter(): + # this access is traced + conn_counter = cache.get("conn_counter") + +Use a specific ``Cache`` implementation with:: + + from ddtrace import tracer + from ddtrace.contrib.flask_cache import get_traced_cache + + from flask_caching import Cache + + Cache = get_traced_cache(tracer, service='my-flask-cache-app', cache_cls=Cache) + +""" + +from ...utils.importlib import require_modules + + +required_modules = ["flask_cache", "flask_caching"] + +with require_modules(required_modules) as missing_modules: + if len(missing_modules) < len(required_modules): + from .tracers import get_traced_cache + + __all__ = ["get_traced_cache"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask_cache/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask_cache/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b2ceaa575880dc65a3af1c31efa95d14c64973e GIT binary patch literal 1813 zcmcIk%WfMt6rGV|OP<=WKY>UWk^pPQ?jwMOC}@!;$f{^l2W@6UQNt^V@eGG0IdK&zZR(8O!k7YLdgP%!SX!&hI7!sXE=##AjRhW;dXa2NB@d@GVv=vG3b`O_WgqOw z1&i0Q2F4K^FEbO1(kVMQB@xnC<|tTGa!jVvxh@OBxra(b;ea;bPNzfya8J<;2;&_1 zcpOCp*WYXX={e3yX|q;-z1fjnv@KY887f(NvfUHy#S4MX0SObmSa<4 zNw3OE6QyAeS_1<3`q9(J^!&wlkG=moSbR@5{d?P;eEp8bC2jPOH19q}jt2L(U+$rk=h)wvX9byyp z()6&hBF9nWXG<<-x`h(yQR9rSy&V?97&Lu*ansI0rro-gjkoKJ;s~SkeV}`2#c#jA zN|Y9t%tCU>bTTh<4%#Hwr^(q&UcEfI|MD|^`ngmm^p}M^JG=hk#I1!&&J!ins2Pi+ z%WHMES{0Bq$zA_9^DEiJ4+*7i8c`an)lKg~&DTRmiP^`k6YsZsttdPS_gjAek*G*< literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask_cache/__pycache__/tracers.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask_cache/__pycache__/tracers.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..faa8b8722b87f3469fee270d78a159c9483f8717 GIT binary patch literal 5439 zcmd5=TXWmS6~^L1g5X70yRp+w(TvlUtwq|kUg~>;O!gY9q7m!fNk|?3FM=Szg#gluAr4Wg#RP)rHW@#dy zf_%zKiG12i_jF5#yiPM-re|1&$QzK)T3M0LLOy5Zh{VW#o)r#=HOG|h{D9M(wLtUM zA}v@;bk1643sk0ZOQG|0;VH3BQMn-%7l-GJH79gvzio!xahd7%DKlGrZniup*tT8A zeZY)YuzsmtBvH!k`>js<748g*XDao|`u*muYQt_+Zr@$6*&CH+&Hkiz9~u`LcPn-K zcCFI5w^3V5Dl?6N<2UyPEOKv^*YQCVdUabRp0mR&}j!y`>xGd(BI)M zvs=91iu&)r;Fz9#uFmP4e zaPnyI)#1fG2b++L6iT*req*={h$^1cBac-gjnv1=XOe$Tl|p&j;Ma+SKhZ-u>TzH4 z<1%pXSX{tjh zlaw;lfRd4vvNQ*!Y*Nb80+ez|X^zfADW8-U=pvK~Nok2LLuu}rq)OI2P<49vew&51 zupJt!Wa&e%?>b&^t<*$|qGE%?|A=iPt8uOA3NjOAjM?n<{Z82Df$8{rW`DrA6L$K3 zVA8(N%&omilPsBcwgSc{K>3YoJ6WxGN z;IRiY+Ccizq=#K@x??VokFhZ{keh!(GLpJdmyAd#cNNI1lss1+Dcbx~ z&cRLw4i9i5a56raF0du!Ho>XsY}d6reyd+R70uZ+cEzze@kkSD#Zv%tq=^Zl`NKZR z%(w*5Kc~%+nkXNy<3^?WNv*!dWvo*`6)ZjJ7DV|&5j_S-psTZD_ZjQ2bp|!s@f~k3 z?6`p)IK6?#Y*0qV(GNzNP+gwEe5SVRvH@->QoMdU(kR>7X&1Ff4Vc%86t_ot0Xvca z4iDkrUrOABny*LY0q;BnspW?bFSq(0WjrVkFP2xg{72mjSGvFA7ccpP3-)f?UtJwu zzJO1AP;Qn7oxw`tj8+~F2djH~Jyxbvpmq2~j}A9o$4cm{zS4C@jDM#6f zd7_LB=a&-%wk>pp{{$yQ8F%3gMAW6`fSw$gC&S`w1VOKp$=L$AK!%9W(D5V$sox+- z1A?v|5~RHaK`9_8eF8yXoF@{*--ER^c^*y*tjvr>hG)k@2)@#k$Tf>@)=a`D@B|z1mg6#R(Y(JKKGPs|D=ws%8P59>t z^9PexLx^e6ON%IF7N4wv$)|>^ZvfQB$ZHlUDxJHveqA zc`?&eY(Id_?;^H%x=Sj$0r099-v(JB{A749mR^T9RFfl4d&3bI!+B{coFcBB z2}rHFUaM@>i>av)3XiD?X;Zxh_yn zoMv{aZoYQ7Bi@k;|8E2d>fEE<|wiFe_ie{MJ5N8MZ(KCR)ja$3roRSNOYp6R02QQI9)K3 zU#6fJGD2EWwcGR{rQR0r$D0#;-#cjuZ0=BvwY;jPS1) zF*pvjev8explJSe6@M3MgGERrU6*CJdg!De=VU`Z+v8QJEuQATflri%t5q9jw9?}} zi;G2fh>2FO!{&vLu z!I1b4Km80RK?E(xi1sKYA`q+yMy$vDo)>%+_8PbkMdJl3gJjes?S4XdplB_KQ@X zC*FVe!sLbCsV4T5WEoo0hjLW27x%-mAi9MU;cN6kz!_}d{^0-9@ddIm9at>|0B2Y*9_NlLS<1d*#jY|5H{0pJwM zN{o18weMv@@1>10 z>nOoz&I@K@i~Pdp;k+?#&cj*LdqKOi#;re!S!ztb@wgt8;87psW2xdArg+h!OF(~Isb+R#O#Eln&xD=|b~m0 0: + span.set_tag(COMMAND_KEY, args[0]) + return super(TracedCache, self).get(*args, **kwargs) + + def set(self, *args, **kwargs): + """ + Track ``set`` operation + """ + with self.__trace("flask_cache.cmd", write=True) as span: + span.resource = _resource_from_cache_prefix("SET", self.config) + if len(args) > 0: + span.set_tag(COMMAND_KEY, args[0]) + return super(TracedCache, self).set(*args, **kwargs) + + def add(self, *args, **kwargs): + """ + Track ``add`` operation + """ + with self.__trace("flask_cache.cmd", write=True) as span: + span.resource = _resource_from_cache_prefix("ADD", self.config) + if len(args) > 0: + span.set_tag(COMMAND_KEY, args[0]) + return super(TracedCache, self).add(*args, **kwargs) + + def delete(self, *args, **kwargs): + """ + Track ``delete`` operation + """ + with self.__trace("flask_cache.cmd", write=True) as span: + span.resource = _resource_from_cache_prefix("DELETE", self.config) + if len(args) > 0: + span.set_tag(COMMAND_KEY, args[0]) + return super(TracedCache, self).delete(*args, **kwargs) + + def delete_many(self, *args, **kwargs): + """ + Track ``delete_many`` operation + """ + with self.__trace("flask_cache.cmd", write=True) as span: + span.resource = _resource_from_cache_prefix("DELETE_MANY", self.config) + span.set_tag(COMMAND_KEY, list(args)) + return super(TracedCache, self).delete_many(*args, **kwargs) + + def clear(self, *args, **kwargs): + """ + Track ``clear`` operation + """ + with self.__trace("flask_cache.cmd", write=True) as span: + span.resource = _resource_from_cache_prefix("CLEAR", self.config) + return super(TracedCache, self).clear(*args, **kwargs) + + def get_many(self, *args, **kwargs): + """ + Track ``get_many`` operation + """ + with self.__trace("flask_cache.cmd") as span: + span.resource = _resource_from_cache_prefix("GET_MANY", self.config) + span.set_tag(COMMAND_KEY, list(args)) + return super(TracedCache, self).get_many(*args, **kwargs) + + def set_many(self, *args, **kwargs): + """ + Track ``set_many`` operation + """ + with self.__trace("flask_cache.cmd", write=True) as span: + span.resource = _resource_from_cache_prefix("SET_MANY", self.config) + if len(args) > 0: + span.set_tag(COMMAND_KEY, list(args[0].keys())) + return super(TracedCache, self).set_many(*args, **kwargs) + + return TracedCache diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask_cache/utils.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask_cache/utils.py new file mode 100644 index 000000000..27a8f2215 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/flask_cache/utils.py @@ -0,0 +1,57 @@ +# project +from ...ext import net +from ..pylibmc.addrs import parse_addresses +from ..redis.util import _extract_conn_tags as extract_redis_tags + + +def _resource_from_cache_prefix(resource, cache): + """ + Combine the resource name with the cache prefix (if any) + """ + if getattr(cache, "key_prefix", None): + name = "{} {}".format(resource, cache.key_prefix) + else: + name = resource + + # enforce lowercase to make the output nicer to read + return name.lower() + + +def _extract_client(cache, write=False): + """ + Get the client from the cache instance according to the current operation + """ + client = getattr(cache, "_client", None) + if client is None: + # flask-caching has _read_clients & _write_client for the redis backend + client = getattr(cache, "_write_client" if write else "_read_clients", None) + return client + + +def _extract_conn_tags(client): + """ + For the given client extracts connection tags + """ + tags = {} + + if hasattr(client, "servers"): + # Memcached backend supports an address pool + if isinstance(client.servers, list) and len(client.servers) > 0: + # use the first address of the pool as a host because + # the code doesn't expose more information + contact_point = client.servers[0].address + tags[net.TARGET_HOST] = contact_point[0] + tags[net.TARGET_PORT] = contact_point[1] + elif hasattr(client, "connection_pool"): + # Redis main connection + redis_tags = extract_redis_tags(client.connection_pool.connection_kwargs) + tags.update(**redis_tags) + elif hasattr(client, "addresses"): + # pylibmc + # FIXME[matt] should we memoize this? + addrs = parse_addresses(client.addresses) + if addrs: + _, host, port, _ = addrs[0] + tags[net.TARGET_PORT] = port + tags[net.TARGET_HOST] = host + return tags diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/futures/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/futures/__init__.py new file mode 100644 index 000000000..9b89dddeb --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/futures/__init__.py @@ -0,0 +1,31 @@ +""" +The ``futures`` integration propagates the current active tracing context +between threads. The integration ensures that when operations are executed +in a new thread, that thread can continue the previously generated trace. + + +Enabling +~~~~~~~~ + +The futures integration is enabled automatically when using +:ref:`ddtrace-run` or :ref:`patch_all()`. + +Or use :ref:`patch()` to manually enable the integration:: + + from ddtrace import patch + patch(futures=True) +""" +from ...utils.importlib import require_modules + + +required_modules = ["concurrent.futures"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + from .patch import unpatch + + __all__ = [ + "patch", + "unpatch", + ] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/futures/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/futures/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2a028cb757b5563fa6c4e1008254d2c61839e006 GIT binary patch literal 966 zcmY*X&2G~`5MDdZPpwvkKEWOvMB^4{r9`FT09O#D2;_^i@lM>W?6ujSq{)E`4}f?9 z?z~d3oGS4I91yd%Tf#_QfA)U!eKWKBerG2n7$@JqvEvRQKi$V>Y~jZXeDpakoWz8C z+|RwjkA08uPUgiO@S*d?`}!yjAkZ&)z`J?3=*C^#4|or8FYY1k^Fijv{SV}ryd~%4 zBjH=`$S`ETJVL^YOQ6%~+#0JuPp4E!1DRq*l#*7etXRejXljs_TBSf5%2Fd1K#gLl zkQq%&Ou07U49pThB2@w7dPJS{rW{D^Opw3~U0x!!te~;dlqsNa4XHK2Lm?@n5|*vj z<0hpMrYV!Pr;rwlK#xknLX=kLE1H3HioomYU=)VoE6HX#)){_oBdmLFJN~agp(!{v zrZa2G0;iC&9A)cbt#&FC1@p<2bJsl_C@W7}jPPtqOGO(-#Y}pcVD|p-^d6s0v7$GM zS`C}DI|}BTk`_$b+O?^rZgFFblSvp-jJYZc+Imw_RHZVsHmY}PwBI)Q{6bk6hPQal z$k4mlRq)9Q1xZnIn}c5O;SsinJ8DyI@oqS7w-!Rsx3Y#Rzzx;K<@IoI(@zp!rb!a- zS|f5jYU;?vO!>%&AGA*V&MMw5gm!bh`|(fCX<^$*@`I>>^Ki@WRa7alz!QxYOhxlD z=b&`7K8nUOxx6|&xq70Gp33Slxz6Nxync4*BCDc{s1ntIklLW;!D3yFSE~Y|c3$^y vcXySTMQztnk_ahGl8ma=&ET||&zT#A-oqt$xZUabp%-}D-gd|L{6BvIo>Ma4 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/futures/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/futures/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..359cb254a82d36217c8257f1a6af275f14e6bbdb GIT binary patch literal 958 zcma)4J#X7E5G5tqvK^%ff_CWU)o5ZRZ4tmgQ549>7z9YVXc1_UaTG|Da6CDQ*7Of_ z>Dax0No%M4g-$)vDUzZcLV?G3M~Zy+?mn-tj~I>p?_b3?kFlTDc{(V@KG8IXWF(Wk zU?opDx7aJZ(oX`yz6^?9*-t{kJ=re?ricscGhAj+wpe=?KU z^zMAvl>Qk*&-uZf&l&obDmrZUCSm*LNQ-%)%=ni^Ku!8A|`=r-}3xxs^ z!0OzEX61Drr5IA=2ZM+fXMQc?8uFz;6)y$E7gZq@n7G}GXLEghwYz`y7WUrhdN;jU z=-I4&ziY>$j?d#duXl27P!z?^vaM&gx21}uL=f5IO4JeZdHldTc1ca^+pg!(ka(B@ z8RkVi;E@L}2tQYT^HhE+hm{}sRUm^q&-M?=-)pxIbMtKd!E!l1N&0`Z1k2Fr2)3n3 z!zS7KSNlgnS7_ay4IAx}+#=`iBsVE?GKrGWV`beN$;$`*rc0$ImDF}9g6&vXBa^&n yFfWV?Q!5+j*_{nC-Eh@Gxn8&$I~~Ek^IvL%4`n5rLVdK|Z)|3fM<0*;!SFXcb@Gt_ literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/futures/__pycache__/threading.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/futures/__pycache__/threading.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1b3402ebf54797c2ac5a2bf7541a270cd39fbf0f GIT binary patch literal 1246 zcmY+E&2AGh5XZeANds*G2?+^N3Exm8+HC<VZoy6d~oZb=FR@&BnVLdy^*G zo`5S7FM#NgSKtMBhrM#*6*w{DO-pOZ7WwEOL=_}OOcH#M#&9~=9a z%?29Dq?fTClfDc-v7RSG*}~G7-aFQ9|G^pTC<&=prWGtvl^tL<`)H(h9$I$lo%-K{ z3kDm@dU(Ok!^)TbLw4T6GLYV9s<$gv#@pB$d4q_jDl z@QJdcT=LQ=$!*R}*`K7A3qH$X%$1vlBdO*>=XRvPiP#Y+A-DMPC^dYBiiHBMv?)Ou zZbyOyw3_jggIrs6W=}ZIOK^=vEup0oDImvX*_=~F4Bc31Qa!{OxS>@rQ8LADJ}7l! z(_C-4bw}Xgh5RAYZPzPOS`dk<+pd86UefFtqwF-53VQeBkub49iK&X0qjkG!*LP#` zKWPsO+sKC4ka4PV8Cj4|H#3pko^O!C)PI`n!k< zNT=v`G!+mH@(g`2(PAgs>+9L+_U`F3*my#hOV=GWvA}EFD(3ZCbDAcK1e94r!|sLNnzt5q(8678-(ZcV z1(+Rc94i&lHogT9E_tiih3B!#tNbeXLd5#w5O3cFoSH(y8cYiuY9_ z@GbGwJ~8NnHAtY76K1ZH5#%~A4GQGN66S6QbooG0fEJp%jsT5#oKjUTj(Tcb=`S2$z_?T*YunWInfC^kNyFL CCS(Wz literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/futures/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/futures/patch.py new file mode 100644 index 000000000..adad3565b --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/futures/patch.py @@ -0,0 +1,24 @@ +from concurrent import futures + +from ddtrace.vendor.wrapt import wrap_function_wrapper as _w + +from ...utils.wrappers import unwrap as _u +from .threading import _wrap_submit + + +def patch(): + """Enables Context Propagation between threads""" + if getattr(futures, "__datadog_patch", False): + return + setattr(futures, "__datadog_patch", True) + + _w("concurrent.futures", "ThreadPoolExecutor.submit", _wrap_submit) + + +def unpatch(): + """Disables Context Propagation between threads""" + if not getattr(futures, "__datadog_patch", False): + return + setattr(futures, "__datadog_patch", False) + + _u(futures.ThreadPoolExecutor, "submit") diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/futures/threading.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/futures/threading.py new file mode 100644 index 000000000..04f60baec --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/futures/threading.py @@ -0,0 +1,41 @@ +import ddtrace + + +def _wrap_submit(func, instance, args, kwargs): + """ + Wrap `Executor` method used to submit a work executed in another + thread. This wrapper ensures that a new `Context` is created and + properly propagated using an intermediate function. + """ + # If there isn't a currently active context, then do not create one + # DEV: Calling `.active()` when there isn't an active context will create a new context + # DEV: We need to do this in case they are either: + # - Starting nested futures + # - Starting futures from outside of an existing context + # + # In either of these cases we essentially will propagate the wrong context between futures + # + # The resolution is to not create/propagate a new context if one does not exist, but let the + # future's thread create the context instead. + current_ctx = None + if ddtrace.tracer.context_provider._has_active_context(): + current_ctx = ddtrace.tracer.context_provider.active() + + # extract the target function that must be executed in + # a new thread and the `target` arguments + fn = args[0] + fn_args = args[1:] + return func(_wrap_execution, current_ctx, fn, fn_args, kwargs) + + +def _wrap_execution(ctx, fn, args, kwargs): + """ + Intermediate target function that is executed in a new thread; + it receives the original function with arguments and keyword + arguments, including our tracing `Context`. The current context + provider sets the Active context in a thread local storage + variable because it's outside the asynchronous loop. + """ + if ctx is not None: + ddtrace.tracer.context_provider.activate(ctx) + return fn(*args, **kwargs) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/__init__.py new file mode 100644 index 000000000..78483e9f7 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/__init__.py @@ -0,0 +1,74 @@ +""" +The gevent integration adds support for tracing across greenlets. + + +The integration patches the gevent internals to add context management logic. + +.. note:: + If :ref:`ddtrace-run` is being used set ``DD_GEVENT_PATCH_ALL=true`` and + ``gevent.monkey.patch_all()`` will be called as early as possible in the application + to avoid patching conflicts. + If ``ddtrace-run`` is not being used then be sure to call ``gevent.monkey.patch_all`` + before importing ``ddtrace`` and calling ``ddtrace.patch`` or `ddtrace.patch_all``. + + +The integration also configures the global tracer instance to use a gevent +context provider to utilize the context management logic. + +If custom tracer instances are being used in a gevent application, then +configure it with:: + + from ddtrace.contrib.gevent import context_provider + + tracer.configure(context_provider=context_provider) + + +Enabling +~~~~~~~~ + +The integration is enabled automatically when using +:ref:`ddtrace-run` or :ref:`patch_all()`. + +Or use :ref:`patch()` to manually enable the integration:: + + from ddtrace import patch + patch(gevent=True) + + +**Note:** these calls need to be performed before calling the gevent patching. + +Example of the context propagation:: + + def my_parent_function(): + with tracer.trace("web.request") as span: + span.service = "web" + gevent.spawn(worker_function) + + + def worker_function(): + # then trace its child + with tracer.trace("greenlet.call") as span: + span.service = "greenlet" + ... + + with tracer.trace("greenlet.child_call") as child: + ... +""" +from ...utils.importlib import require_modules + + +required_modules = ["gevent"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + from .patch import unpatch + from .provider import GeventContextProvider + + context_provider = GeventContextProvider() + + __all__ = [ + "patch", + "unpatch", + "context_provider", + ] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..276c580cfccf5c1a250df33b067af9e8e54c5234 GIT binary patch literal 2223 zcma)7O>f*b5S8}Z8wbv%K#zob*)?jd)3iZt*hYiINDBmR+PZC^i^LVVG82kYNov>Y zLoYe?RP+z@-oNB)Px%W43Ur2&)@#@O;KDK`4QJlGc{BR<{{5i`>+!F@(9?nE{f;;5 zYXfdRgFiEa3-dyc`K%@VtQEF=kF`@jZ1Waxzx97U4m-SKUa=1A%5K&TyYRllda&<> zJ-6@6el`dPzQ+e|y>NpK*ajP>?QroGVL& zvOLw)id+%Om?5Stid^|= zhdDYYNeNML<2BL|w#4r0a-1K-u9I;{7wJ&uVm zWXcg+X*eT>TN205o<+}3zB@TRi(WoCd-{3w!!D=C-1h*uC2jG<&!l{-^+!R27m`V__g0IsgH%%@a3nQ#rv7)w>+fJ0RXtu;1m%3kPvE*MxsW~CKU zT!JmgrFnmcEI6X|4q^y}oYy1(b!h^Uwc>n;5fnnGfD>V%%=R3F=E$3A@Lek_ zKpMfnObTy3}~2ay+WDSeYH&elUivIZ<>fp%%j*>-G0W0#$IdLIIYL zIcN-Jup|o4Y_wX#@~_u*=PjP?CeW9O`!jmDJ;G#AtdYEinV6&6>gwRXRY zzFL7*2RL$F$Wg`S(HV3iQn0ggijBUrgFXQQCI^kcAzz3K;xD)cL1%DZD;q6Pzo?bE zG)Y5Lk2_ z)B(Lo7`j29@7nO`8~ayVNU+d6%5qjpZo*#0*gL;^-*x2%!+xon?R(GN2kdF}NqJe1 z7Ttjpx(k=92ep#J7!>>i+fnqJr}69WE`MB13N7Z;^2wa)$t;&J1I^^};becR7O(dnzy3%+ ze58uK=pt47`0*BbgAe-nSLUc2SP@6W#gEPTqV literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/__pycache__/greenlet.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/__pycache__/greenlet.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f38137fd2b178b50794837eda1c2192d41ce492b GIT binary patch literal 2752 zcmbtWPmj|^6rYKm#K|UM1q+mlwmzYzU6KVVq@@)qLaP-AmI?x`d~s$ylf+racE^+K zABEFWaj7cx3sgnwWj{v0hOa&G6*%?1u^qC~u7qHud7gQG^X9$Z`(r`5mj$Do9lxDl9>vQwr+kZ}cM%->4ah*RAryv!@F z2(Jk1*l=pFuW}3amZ*Vb{n&I?fLG&n;MIX=j2pmfiYi~(D4cC`i!(!--4xN=Pd==rs?HvF%zMB7==n4sGnps^SO|34S4IJ z;POBwj^sKJK^)47Q+iDXlv7@MWjV%cViL!MON_M9lg#tO;bZ^64?VoB7zmjO^8(D| zJ`hFzrfeyr--=L7BOEjNp=I|D zYiVVu-Eitdp}eV-;8I$JHB(ATwA*RA51;Jxwtnfk4|=_)X*rq3LP|6ywOrQ^edW4o zHOY>&%c+@&V33+j4wJO9w~tS!maBG$so|-E6Pngs*9%yZxb6vgchHTcKVwRCXH0en zQ2`!$h$lxH&tDHxCad zqRY7g4BtOJh>`SnyP2!qp%fwvgzChH5|eZVW}>sTWYLy!(J<-J<>e@J3O7&6=yJ#= z0$i@U?j+*V0M`xI{dLNM?2T5MHgg9(5d^L)FTy1mS;f^QB#0s2o0Nc9RPWEv9UFv- z+~E{+2J#1r1oFq!`BR4cG1C@WF8FrtBzptO>o$yjzu@)zw#MF8Bc^OWv8RdPwu)>i z;s6ckZb?N(AcxV?PvHfr)q^}Zf0>%w$%O1#Rq8Ty6{Im-{I$FukBN5un4#obG8Zaql z`ysfsI6Pr~xR6-SPw3nUJ)t#4gHx8HgHk|Na&%YNrxYM;VWey}7zhtU?A=2!5tzGT z#AbdZJ9VuS4oZ;;gNYf;xs#cE9*HQw3vdUZlt1y0#KPJ{t14_w*5MM5LC}^pV4#H5Z}q!%8)6_rjv>Uv$g)9O zbVf=|1Pha;6KtxY2}Iq43OxkdPG@JopT*Ey?sM36aOysH1r~}ZsykY#=xEr#WCdQe z4OxSSyoLm8>BNu?TsM)lkm!2T<)@1dg>JyqMMvn-)zcO&YQ0^Nm+?3nAg=;RtFFr< z4{8}z$j^|RRn=$;zD%%JThwf7<2=}$8t5U85BVls%4~e}n8nY-NJ4iM{F5QWZ&BvT z=eRlprf<`egE^)pOZU?ta3!&zY9CEvg-S{&}%C5cKU;!~`V@ktQX-`n@m$UyGs zWcmWF(?LEDnL8knBlB)4I3EBA>b_l^hMtf6PjNg`@Ui71zl7`Md0u{B*h#4T3I)#% zGkS5akhW-hRaRl=tYp2S6Gu^ynn<1UP(Q3V|6$Vq7-?e$AAI;S!0*H$a$4J4FSd4i z?u)Ib&mKM5Nh{Ir7@Dm9J~~#>&2(8g%~M4oF~1xWc^a6doLqE()cPU&Zr?}L669O7 TQEC`1W3{z*ajm>+T(SNK-X?;_ literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..63d962918cae5e2ae4544c204b53e963d1693d5d GIT binary patch literal 1951 zcmbtVOK;pZ5Y}V$Snb13Y^ToyuNx#@r!4{)D2f7(YvfQjNbDSRk!g{;rlOSuiduUY zzNP4`hxP~bo`2Cpe}%3+`7iX;8A`i$eCVZIXl6(bhr{`1W={u$z<}}mkKgEDzG3{0 zle@!$$@lP;zXM?gGgBjMxY=?mw3x;0)Sh+1j%lzCb5dvKhOU-fkiF2;vInvs`daou z?uK10cj0Uh2ByJ{q!;#q4w7NGq4{R`fCa4gvk^XIeKr8T#fEGH_%<`&7^BS_@XI*9 zv4M`v`q2wMp;f95@=Wn7^-AV*!MMCJo9*Gz;oHOGlj!Z?>o+fdI)($o6G>yvjwI(< z$`z~x&C1IkX#umX+u3QBOR&WmEbkogInVB26QeJ8M+YVc@RbCJGL)$-WxuygLv<1- zah3NTBKoTdQLx}GHiZ}%0fF&~s`!Ga3r?o`A~MTam2y(%ut*i5X-XuYfHzspOQLX@ z>yrW=E;E{vv$NHs&dx}j(y}B)4!bLDPo|Tov z%g_NaA(T`lmjuGhGlt9jqdRYHJ!$to4mEfMw!s7=6z(7eg~zDI(_MY|gBfnd4UkdM zB6&RWYd?~_NC7tKLRh7T1ajA?J;oHmS-YCax(mlAVhV1{E+~93<#_pQynmiuCVS75ujI3@vtlp0nr8d^%Ww8@OpEc!xDdrtk(CNScsgGe z`-{blkK3!)937Y9eB9tSE)dY&Vo^I<+5!(s%nqa5H{}E1|2KD*f9jaM=B3Id*AdU9 zn2P(UuuC+ilPYUaomHhG=bVUap1Yc^MXS85-6+CxtT!6IE=1V} zO*ud^L}DXZBiu-vuvU9jro&EP(uCcGuPod-gb*s6?A{4NvqvkojIYu>MBjaEP1w4xRz+l05de?qt~9N`{& zQJd3U(hJ_fIb`RZ3$jP6)u*u*Te&o1ZhlqyEagJs@{h5Jc|LA-o~83t!m_tp#4^DH zt`0m3hkgP=kce^;Ih;l=cOs9w+&dvri??_i=Jp#hqTJ`fNf7zG!@IB+ydfTmIuP8g z>fW}P32ClHRQ~lWmho8V(CmvaPE0xzVNypOj&c=_tyL)IBAJ>rm;G)xgfZMtbyzgl z3b`nRFnRE7!K1$bVaO>xBd_U#a(dWN4~(;*3v%FcdhG5( z&H);y{N`to6+)r6F*>~g35*QalPO$Jnu`egJ3X&z*C170ux>u6e9&WKRJF{ue%TrR z^!t$gFdRPbIhChHHmW+!hiv?osAY)s-}#_W=`1#4FpJe-lxIK~Jt!XyHebkB2WuM# z>*~>CS*)@7SZ;2XU$5a97lYxTNQ;N5)F#fdhqJQSJUW_)0p~`=$>of2sr1F5jum{> zlfdDiUQ#eYm>8RsB~TXRl$-(hhsaE) zT)%{$3nSDd1q!4i#9M))fdbc<38+(b8%~1h7EBhvTCBipwaWHm&1yEE^W-zw&=>}B z%H9WV@_(zuTA&pSEJtJ)&Uynx!!Bn5MG5R$2X;Bgt_84*662ZHf*d=i?FBuflq`U4 z4dcGu73PB_ca>*qDkT=u-rjao6MK7bdtdqBUSWDFU)M}i*zT&9o&uFs8}+~<0F3In z7N*K?a;q>4Z)w$GEXiW68M_2et-{{t@MzmUo8;f2<%63+JNNNk$`Lh0$)-F6rB3;4 zYM>I0bL^=5umU)p0Vaq`kCu<03o}*nBd`D!F8WY7%JNtJo@Z%LbyIB%Kqf+AUyoMU zw17q-8WQxJ8UQyj^Xp`3*F5pzfhSI@@Gosx_u9pq}qJfggBF vf!%4y5C!KYtq^&IM1@`he=8Od`{|XdTNk!if+uzC-(j6v1OjJ?!q5E=*rxO$ literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/greenlet.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/greenlet.py new file mode 100644 index 000000000..614096488 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/greenlet.py @@ -0,0 +1,60 @@ +import gevent +import gevent.pool as gpool + +from .provider import GeventContextProvider + + +GEVENT_VERSION = gevent.version_info[0:3] + + +class TracingMixin(object): + def __init__(self, *args, **kwargs): + # get the active context/span if available + current_g = gevent.getcurrent() + ctx = getattr(current_g, GeventContextProvider._CONTEXT_ATTR, None) + + # create the Greenlet as usual + super(TracingMixin, self).__init__(*args, **kwargs) + + # copy the active span/context into the new greenlet + if ctx: + setattr(self, GeventContextProvider._CONTEXT_ATTR, ctx) + + +class TracedGreenlet(TracingMixin, gevent.Greenlet): + """ + ``Greenlet`` class that is used to replace the original ``gevent`` + class. This class is supposed to do ``Context`` replacing operation, so + that any greenlet inherits the context from the parent Greenlet. + When a new greenlet is spawned from the main greenlet, a new instance + of ``Context`` is created. The main greenlet is not affected by this behavior. + + There is no need to inherit this class to create or optimize greenlets + instances, because this class replaces ``gevent.greenlet.Greenlet`` + through the ``patch()`` method. After the patch, extending the gevent + ``Greenlet`` class means extending automatically ``TracedGreenlet``. + """ + + def __init__(self, *args, **kwargs): + super(TracedGreenlet, self).__init__(*args, **kwargs) + + +class TracedIMapUnordered(TracingMixin, gpool.IMapUnordered): + def __init__(self, *args, **kwargs): + super(TracedIMapUnordered, self).__init__(*args, **kwargs) + + +if GEVENT_VERSION >= (1, 3) or GEVENT_VERSION < (1, 1): + # For gevent <1.1 and >=1.3, IMap is its own class, so we derive + # from TracingMixin + class TracedIMap(TracingMixin, gpool.IMap): + def __init__(self, *args, **kwargs): + super(TracedIMap, self).__init__(*args, **kwargs) + + +else: + # For gevent >=1.1 and <1.3, IMap derives from IMapUnordered, so we derive + # from TracedIMapUnordered and get tracing that way + class TracedIMap(gpool.IMap, TracedIMapUnordered): + def __init__(self, *args, **kwargs): + super(TracedIMap, self).__init__(*args, **kwargs) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/patch.py new file mode 100644 index 000000000..0db7ec0ce --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/patch.py @@ -0,0 +1,67 @@ +import gevent +import gevent.pool + +import ddtrace + +from ...provider import DefaultContextProvider +from .greenlet import GEVENT_VERSION +from .greenlet import TracedGreenlet +from .greenlet import TracedIMap +from .greenlet import TracedIMapUnordered +from .provider import GeventContextProvider + + +__Greenlet = gevent.Greenlet +__IMap = gevent.pool.IMap +__IMapUnordered = gevent.pool.IMapUnordered + + +def patch(): + """ + Patch the gevent module so that all references to the + internal ``Greenlet`` class points to the ``DatadogGreenlet`` + class. + + This action ensures that if a user extends the ``Greenlet`` + class, the ``TracedGreenlet`` is used as a parent class. + """ + _replace(TracedGreenlet, TracedIMap, TracedIMapUnordered) + ddtrace.tracer.configure(context_provider=GeventContextProvider()) + + +def unpatch(): + """ + Restore the original ``Greenlet``. This function must be invoked + before executing application code, otherwise the ``DatadogGreenlet`` + class may be used during initialization. + """ + _replace(__Greenlet, __IMap, __IMapUnordered) + ddtrace.tracer.configure(context_provider=DefaultContextProvider()) + + +def _replace(g_class, imap_class, imap_unordered_class): + """ + Utility function that replace the gevent Greenlet class with the given one. + """ + # replace the original Greenlet classes with the new one + gevent.greenlet.Greenlet = g_class + + if GEVENT_VERSION >= (1, 3): + # For gevent >= 1.3.0, IMap and IMapUnordered were pulled out of + # gevent.pool and into gevent._imap + gevent._imap.IMap = imap_class + gevent._imap.IMapUnordered = imap_unordered_class + gevent.pool.IMap = gevent._imap.IMap + gevent.pool.IMapUnordered = gevent._imap.IMapUnordered + gevent.pool.Greenlet = gevent.greenlet.Greenlet + else: + # For gevent < 1.3, only patching of gevent.pool classes necessary + gevent.pool.IMap = imap_class + gevent.pool.IMapUnordered = imap_unordered_class + + gevent.pool.Group.greenlet_class = g_class + + # replace gevent shortcuts + gevent.Greenlet = gevent.greenlet.Greenlet + gevent.spawn = gevent.greenlet.Greenlet.spawn + gevent.spawn_later = gevent.greenlet.Greenlet.spawn_later diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/provider.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/provider.py new file mode 100644 index 000000000..13b05d3f6 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/gevent/provider.py @@ -0,0 +1,42 @@ +import gevent + +from ...provider import BaseContextProvider +from ...provider import DatadogContextMixin +from ...span import Span + + +class GeventContextProvider(BaseContextProvider, DatadogContextMixin): + """Manages the active context for gevent execution. + + This provider depends on corresponding monkey patches to copy the active + context from one greenlet to another. + """ + + # Greenlet attribute used to set/get the context + _CONTEXT_ATTR = "__datadog_context" + + def _get_current_context(self): + """Helper to get the active context from the current greenlet.""" + current_g = gevent.getcurrent() + if current_g is not None: + return getattr(current_g, self._CONTEXT_ATTR, None) + return None + + def _has_active_context(self): + """Helper to determine if there is an active context.""" + return self._get_current_context() is not None + + def activate(self, context): + """Sets the active context for the current running ``Greenlet``.""" + current_g = gevent.getcurrent() + if current_g is not None: + setattr(current_g, self._CONTEXT_ATTR, context) + super(GeventContextProvider, self).activate(context) + return context + + def active(self): + """Returns the active context for this execution flow.""" + ctx = self._get_current_context() + if isinstance(ctx, Span): + return self._update_active(ctx) + return ctx diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/__init__.py new file mode 100644 index 000000000..357f1b184 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/__init__.py @@ -0,0 +1,88 @@ +""" +The gRPC integration traces the client and server using the interceptor pattern. + + +Enabling +~~~~~~~~ + +The gRPC integration is enabled automatically when using +:ref:`ddtrace-run` or :ref:`patch_all()`. + +Or use :ref:`patch()` to manually enable the integration:: + + from ddtrace import patch + patch(grpc=True) + + # use grpc like usual + + +Global Configuration +~~~~~~~~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.grpc["service"] + + The service name reported by default for gRPC client instances. + + This option can also be set with the ``DD_GRPC_SERVICE`` environment + variable. + + Default: ``"grpc-client"`` + +.. py:data:: ddtrace.config.grpc_server["service"] + + The service name reported by default for gRPC server instances. + + This option can also be set with the ``DD_SERVICE`` or + ``DD_GRPC_SERVER_SERVICE`` environment variables. + + Default: ``"grpc-server"`` + + +Instance Configuration +~~~~~~~~~~~~~~~~~~~~~~ + +To configure the gRPC integration on an per-channel basis use the +``Pin`` API:: + + import grpc + from ddtrace import Pin, patch, Tracer + + patch(grpc=True) + custom_tracer = Tracer() + + # override the pin on the client + Pin.override(grpc.Channel, service='mygrpc', tracer=custom_tracer) + with grpc.insecure_channel('localhost:50051') as channel: + # create stubs and send requests + pass + +To configure the gRPC integration on the server use the ``Pin`` API:: + + import grpc + from grpc.framework.foundation import logging_pool + + from ddtrace import Pin, patch, Tracer + + patch(grpc=True) + custom_tracer = Tracer() + + # override the pin on the server + Pin.override(grpc.Server, service='mygrpc', tracer=custom_tracer) + server = grpc.server(logging_pool.pool(2)) + server.add_insecure_port('localhost:50051') + add_MyServicer_to_server(MyServicer(), server) + server.start() +""" + + +from ...utils.importlib import require_modules + + +required_modules = ["grpc"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + from .patch import unpatch + + __all__ = ["patch", "unpatch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7787612288e9d02beaf4aa7f10cc98d73c1655ce GIT binary patch literal 2337 zcmc&$-A*Gn6rO~Hup_qg5iItCq#_g8E-MiM+7+M`7u(X1{wQrV(#}wOw41^?}s6RF_EG`-Zx@JDWoETdTwBzKaY2!!CRBiUJV%61aFuH`0oW|(o?OP2#vB+2xFFTx6qpgERYHn6!nqP? zY|ho_lIf|1C`nFE;x{0i_~LZ%)!FfBk^q}CCIv4*Fy@>Ymdw!WPCaqX`zT2^P2^qY zd^1V@KFioi@4qGM<@>Lby~$HZlipnfP6rPIu|&axdk+Q|mW3fa^F%yC8b%yLft(a7 zNi8Y&3K?n{2rQ}Wrc=y0&CwVuVAoUxRCkyp=Zph$ug}jGLigG>6j^CMBz)Mk?bN@G zhNf3YC-{4105GAnQc&Entzd}`{f73kMg%k!$%xCKWE`0WwCp@K2Jjax%xslK$1a`i zh1d_bin$4SYuhz1d3eWd#~x$FYpfy=0Zjq5*u&D^$_4bEsZhGVx3jaiyVXHhA-~$U zyG<_7P%0_bPz$;mE8i8NHA?zJMU_@d!xAg?NPV9rEbCw~ z`q-aMmWOW?Oqm;c@!{q;a~9lmar}EA;Xe-d_;c1PC7Xe!d(f|Y6Oj`tRqtxIw?F2W z?{;3kdoFiB<>gNNW5)OQuU_n!k(Iq+uViJ{cGEoX&aTS+`MjV#e?=~Tp+RZM#yyjA lF9ztDj^n7DU#}fFIF1Ys>LYkG-mb5#*27w(wq9GW{{{XW_k;id literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/__pycache__/client_interceptor.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/__pycache__/client_interceptor.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..91e77924230f23386c208f57bc5db00601f60e82 GIT binary patch literal 6788 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 Aw*UYD literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/__pycache__/constants.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/__pycache__/constants.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8efa1051b0e6707796b997ade05827e3799c7257 GIT binary patch literal 1202 zcma)5%Wl&^6m^v%cumq`b3#d{ggfNL|tu{`uor>O#HO(Cox5?PpPD=a@ zZ1@58{8F~8_ytysJr+&cEEvnY zZo)Q}p@LhmgWIr+71+Za*vE`1J>UJ!D@l4(?)hG~I;3IW#i~cbepH!9x;Lap=fQyb zW2Z~!_cAiPhuk;}Q%(?)a1>O#F4AZ((f8?>5e>q)GC>lOYP6r?f#8~vwi=yt)izb*LN#Ey-Y1OF0X!ho&ox!IU?*$Jc3Bu+qF4Sz(GE}9h=_d;)LUV@lB=I$?@?L9b zGlYsTo_>SW(>y5Z{*?SYKV8Z6=T`Q7XG}sWkBKjTb_YoPK#q>&+7-LKK0LmD=^wpf z-l6@q$7;3c^te1$z(`n#Ly?*yJy#Bq3>MD@y{vqfYVtR eRK3Zx_-5$hXh7d)xR>AspNiQ+rjW_~$MqLYu4dW* literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1e7587368946b2cb28669d50b96f6e8a130ac37e GIT binary patch literal 2860 zcmbVOOOMn>5bm};9?x^%FP6vh$YYYgKoTLXgh-^#5`oBM#V#By6wA21gBOp-D);OL zO`?obN2y3!M4-U{TckCEq$)7IiWAyLN_eRo;2!U72{r zf>`{G*{fnnEMvbWR>Ugy>tao;W4|Fb#5wHGiA`}H`%Q5{T*UsoxFoi)zaaQNYg~SV zNLj1FGkq_Jurs}GbQrWzTR8zYb`E=yCxdR}kQMvzhU0u3E#bUGx{*xKDD-3yCU5k7 z$m~Tl8T}g`_^$LFFAV%hIzc3T@cdYI0S6bxrsF+#qsR}ZRwOjhEU@gw(H=!?vtP0!5zqX_8?N{I4q^lh_51MfhUk`}SFuZ>JD&Btivg0>}APkcp87Fj2 z0@769bR&M56_j?BFc>HpH#sa}|8Y>Ej!O{ioYRYD;7%Ml;0fGZTf}u)ak%JnG$voM z`k}9&Z&ZXoK}90jN1EV5>+7&cluLc+(fhc?q|s-C0(s6jTBa~WK^D~r>E@JPlot8v z84at2I2_?hS9IHX^zI!1Zg`f>Bg?+m1I)=h=d${&4HcZ$C}qXAFI_1COj@@3Q{drtP!5yjdzsVGAFg6^G{n(3x+qVU>WycccrW6n0QefZ~w@9My&V92A+Q z>UHG#nL#r%^Ti4Ec5%4n;TpZhc-~E9X2e~P_VW0^!;Bc6tM77l30FPEHsq{~TL2l; z=CKL@jm>aIdvMIzFK7@`QajR-DjXp9dc&fiJ;XztB?1fH~) z)Y(B5@j%}b*Q&oFa`BVi^n?*%mfu5pCZ8&uVTX#BL(F^^%R3Q>7 jio#>Q%L`kGB^$f46B8%~6o-=1Hwq zW_UjP%Mb1^Cm8z&eH?yFeB42a{thCT&^pYaTJLCG-O)8_8y%x-I;LuyokG`gEVK*4 z>J+=SW20RZHrgepgmy`kJ9fA1lvTTecEzcvb`|ZaQ&sI6+BK)9+9REj?x-`0c0r7G z#=7IqIND=k{A=b+hzT)?dQwb@Q>drzGjUo>f6c_SZ?#YD8qR6-oDnnVnekbB8a-!( z)?)RuZ@>{-s%xp`^}^L)4XtwAwwo7~)OCC+kADT=`oqc6I?BXCCxN*=6)2a1rfO~GI z!v)xY0G)|LtPLl;)>5M%gs<7Z{xI8@!KNGgvzxA*UF~&*FQeJ*o3po8!WZoupS6D~ zZ+;r~Z}69E;jLTSKf6KCT7PzVwjcDb1*Eu6=i26W|JK%4*Pj(ascyffy}_Q5A$~mS*d=WolJ@`xl<{aa4Z7LZ5ef zU^=>e{iM~P)@g*w zJWBKwB!MGulw=cT0w>?qWiw%IZP=$%yKU@hJ9?b8O|%W6CmJs#+MX2`6XO-YK(*~e zr?z0q*iwr__z-0QyVOf*aovsFIF|Apz9m^-YLdUD#i1dj1z*aZOskMzjDIDM-8EUE z*(B+@nT`wvOIq_^#;F;>P2_tt<9#AZNQx*So#B}|<@)enl2)O;E0{dg-g&Kx*Fuq( z(d*H<@`vPPqj)G$6J-1D_Ef^|vEP+#4^0tN^$U@)KU!jf- zR+)h+0SW|(dbu7PI3&(f-`G12!m)HK#`v@t&tRzwXV3{hB{r{bjGD0 z^?^q|4HYzV-33&-5we!K_l9=X%XgnX@|MwdHis%STGc_AR7y&KAl;y|hBfgJ3 zxp1O;==PZdGja3-tZ@us2oqRrQVc*{ApA#d5%!}lii)VBw!eW5IwhD%O_IHOBN5k-#3gNw${Bu#gs_K_3=G3#K-Q8wWAxsmTE(>8YV9 z$Rp4L!JCoaS6fG{nRp!&W50+l%TI&Ktko{5ueN)k~13MH~Ym^KE}*}m`&{JWkX z!Xp>RA2MjNs(F;?24EaM1~1zwC=WB*Gvdn55E$tWI6irEOjpZ+fHjmH!1Nt7w_QTli9)GM}QC${qS zKdXOFCs+}q(+Lzg!{RgT86d)6v#(GWY^S77dshAp^Uq~%+HbqLeF+*d67x^mP8t2@ z+waML#vdf*q?8nQjh!<59wrMP!ddCDa<>$s<=Rle7vtJv%P%x97^ zjHW=*rwkzF00P8sXH@#n2YwXub>F4 zegZ|Lmhv>2nj-LG`4ex%&?dl8Ay>GuD@k+Cc^5%|f)W=2a4J|C#J@evv@?>8UMI~6*8ej+Q2>vTkTJTR|@iE@<41tMf>&IJ9(RkHBK!jei9 zoO8$ep3Xgd(%`K|^J$~$%&cKGH*`B&ao|Nfa=ZPG&jA^}q|7fZ2ayVoAtZ5=b{K7C z5|Z;dyy7g8V-HUir4_DV1m3|pqIM2Mkbv)YgK$k=q0q!nY%D)`1kI9qpDfKapUGR8kyZww zE4PrIg1$DCBqebqe@)~l&+<0<+9;7q^|bBF?>hUTMSmO~F22P>W{AAC+-%G(E-c;8 z*r{eaXI3LgKgW5vgTC$|9{8k6HO~uAFe3w|-cv7bG1+-Xqc=LJ5(4K;!_44wxoEzCcko5?h0AsHZ-YMV#XB zI+lgZ>ETy)MMB`Z?)AETH&%BAXCxop^h1HHmn4#A@Tr2b3Tg+p1rN6&olc*@((sIfY>@+ TDXJNzR`we9q+PPB6Sw~Z^&T#J literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/__pycache__/utils.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/__pycache__/utils.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0bb220fe91984a3ac85be834bb4b6cd609b22357 GIT binary patch literal 1969 zcmZ`)Pfr_16rY)0uh+&vW1&#gR+@f z#JRu=yohs=&+rn?Gn_VvHG73RN!_ApF7hDuuPBNr6A05 z2;<6f7-iZr?twkKOCisKK|25DzXSFRY& zARkb-wA2V<0i7r{AdZ5LNVUKZ5uVA^y7I0wKl!RV`RL7V==J^g1ZGj!huDswbK(i# zKJmal8U);j*d9Hy*WQGu-POn4@8Qu8VYKT0+z!{)MnA5~){g9^9d)9I9Z75uJUkgi zYiDPD-{xF^*ZOxx>e0?Ob{nFWJrtcFUWv}qJIX)ql(92tORPdizpIsKiIy46Bi53y zzixS4+<5FQ3JD!+f+X5GeXWi4ac*oR8YjI1ETb>cTU{;DE-|PQU7=4WsGjHw%_+1w zL32q?p@u@c6Vylyg))WyGC}hR#sdZ`X^{7{Zkfu~w1i9x7aijYuMN^tXW{w&-iEtd zYi_=H=I&LSo9=e)m9uyo-Po=^uYJ1FsO=wYZDbp_^wg`n|AB6A)t^ZvRzB6^$O}^= zGS*ahR-C5_j)mas(*#QxscEm9(%B|vo7YzLxzyPoo4%%t{)PoSKX=+NZ$%+TEyjVu6 zqg8Qg^zcAC1{uMrq3RJ=s^A_XANzL^D=Pk@U|!1?xw2fl^7<;CjVm}1jQt`r8N=Do zzeI;KTEuK9>t8cD!#8-wDy4a}t&L_mi<3&ZBKd&O3szc#K9=q5rCBhi1^n0!Uhp9` zT~|KBb>S=2r0R{zEfrBp`;^VHvV+Q6^GScehk^f8{&B<-GFge0w4!Ftne(Rk51}RI AX8-^I literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/client_interceptor.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/client_interceptor.py new file mode 100644 index 000000000..7f433fbae --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/client_interceptor.py @@ -0,0 +1,265 @@ +import collections + +import grpc + +from ddtrace import config +from ddtrace.ext import SpanTypes +from ddtrace.internal.compat import stringify +from ddtrace.internal.compat import to_unicode +from ddtrace.vendor import wrapt + +from . import constants +from . import utils +from .. import trace_utils +from ...constants import ANALYTICS_SAMPLE_RATE_KEY +from ...constants import ERROR_MSG +from ...constants import ERROR_STACK +from ...constants import ERROR_TYPE +from ...constants import SPAN_MEASURED_KEY +from ...internal.logger import get_logger +from ...propagation.http import HTTPPropagator + + +log = get_logger(__name__) + +# DEV: Follows Python interceptors RFC laid out in +# https://github.com/grpc/proposal/blob/master/L13-python-interceptors.md + +# DEV: __version__ added in v1.21.4 +# https://github.com/grpc/grpc/commit/dd4830eae80143f5b0a9a3a1a024af4cf60e7d02 + + +def create_client_interceptor(pin, host, port): + return _ClientInterceptor(pin, host, port) + + +def intercept_channel(wrapped, instance, args, kwargs): + channel = args[0] + interceptors = args[1:] + if isinstance(getattr(channel, "_interceptor", None), _ClientInterceptor): + dd_interceptor = channel._interceptor + base_channel = getattr(channel, "_channel", None) + if base_channel: + new_channel = wrapped(channel._channel, *interceptors) + return grpc.intercept_channel(new_channel, dd_interceptor) + + return wrapped(*args, **kwargs) + + +class _ClientCallDetails( + collections.namedtuple("_ClientCallDetails", ("method", "timeout", "metadata", "credentials")), + grpc.ClientCallDetails, +): + pass + + +def _future_done_callback(span): + def func(response): + try: + # pull out response code from gRPC response to use both for `grpc.status.code` + # tag and the error type tag if the response is an exception + response_code = response.code() + # cast code to unicode for tags + status_code = to_unicode(response_code) + span._set_str_tag(constants.GRPC_STATUS_CODE_KEY, status_code) + + if response_code != grpc.StatusCode.OK: + _handle_error(span, response, status_code) + finally: + span.finish() + + return func + + +def _handle_response(span, response): + # use duck-typing to support future-like response as in the case of + # google-api-core which has its own future base class + # https://github.com/googleapis/python-api-core/blob/49c6755a21215bbb457b60db91bab098185b77da/google/api_core/future/base.py#L23 + if hasattr(response, "add_done_callback"): + response.add_done_callback(_future_done_callback(span)) + + +def _handle_error(span, response_error, status_code): + # response_error should be a grpc.Future and so we expect to have cancelled(), + # exception() and traceback() methods if a computation has resulted in an + # exception being raised + if ( + not callable(getattr(response_error, "cancelled", None)) + and not callable(getattr(response_error, "exception", None)) + and not callable(getattr(response_error, "traceback", None)) + ): + return + + if response_error.cancelled(): + # handle cancelled futures separately to avoid raising grpc.FutureCancelledError + span.error = 1 + exc_val = to_unicode(response_error.details()) + span._set_str_tag(ERROR_MSG, exc_val) + span._set_str_tag(ERROR_TYPE, status_code) + return + + exception = response_error.exception() + traceback = response_error.traceback() + + if exception is not None and traceback is not None: + span.error = 1 + if isinstance(exception, grpc.RpcError): + # handle internal gRPC exceptions separately to get status code and + # details as tags properly + exc_val = to_unicode(response_error.details()) + span._set_str_tag(ERROR_MSG, exc_val) + span._set_str_tag(ERROR_TYPE, status_code) + span._set_str_tag(ERROR_STACK, stringify(traceback)) + else: + exc_type = type(exception) + span.set_exc_info(exc_type, exception, traceback) + status_code = to_unicode(response_error.code()) + + +class _WrappedResponseCallFuture(wrapt.ObjectProxy): + def __init__(self, wrapped, span): + super(_WrappedResponseCallFuture, self).__init__(wrapped) + self._span = span + # Registers callback on the _MultiThreadedRendezvous future to finish + # span in case StopIteration is never raised but RPC is terminated + _handle_response(self._span, self.__wrapped__) + + def __iter__(self): + return self + + def _next(self): + # While an iterator ObjectProxy requires only __iter__ and __next__, we + # make sure to also proxy the grpc._channel._Rendezvous._next method in + # case it is being called directly as in google.api_core.grpc_helpers + # and grpc_gcp._channel. + # https://github.com/grpc/grpc/blob/5195a06ddea8da6603c6672e0ed09fec9b5c16ac/src/python/grpcio/grpc/_channel.py#L418-L419 + # https://github.com/googleapis/python-api-core/blob/35e87e0aca52167029784379ca84e979098e1d6c/google/api_core/grpc_helpers.py#L84 + # https://github.com/GoogleCloudPlatform/grpc-gcp-python/blob/5a2cd9807bbaf1b85402a2a364775e5b65853df6/src/grpc_gcp/_channel.py#L102 + try: + return next(self.__wrapped__) + except StopIteration: + # Callback will handle span finishing + raise + except grpc.RpcError as rpc_error: + # DEV: grpcio<1.18.0 grpc.RpcError is raised rather than returned as response + # https://github.com/grpc/grpc/commit/8199aff7a66460fbc4e9a82ade2e95ef076fd8f9 + # handle as a response + _handle_response(self._span, rpc_error) + raise + except Exception: + # DEV: added for safety though should not be reached since wrapped response + log.debug("unexpected non-grpc exception raised, closing open span", exc_info=True) + self._span.set_traceback() + self._span.finish() + raise + + def __next__(self): + return self._next() + + next = __next__ + + +class _ClientInterceptor( + grpc.UnaryUnaryClientInterceptor, + grpc.UnaryStreamClientInterceptor, + grpc.StreamUnaryClientInterceptor, + grpc.StreamStreamClientInterceptor, +): + def __init__(self, pin, host, port): + self._pin = pin + self._host = host + self._port = port + + def _intercept_client_call(self, method_kind, client_call_details): + tracer = self._pin.tracer + + span = tracer.trace( + "grpc", + span_type=SpanTypes.GRPC, + service=trace_utils.ext_service(self._pin, config.grpc), + resource=client_call_details.method, + ) + span.set_tag(SPAN_MEASURED_KEY) + + utils.set_grpc_method_meta(span, client_call_details.method, method_kind) + utils.set_grpc_client_meta(span, self._host, self._port) + span._set_str_tag(constants.GRPC_SPAN_KIND_KEY, constants.GRPC_SPAN_KIND_VALUE_CLIENT) + + sample_rate = config.grpc.get_analytics_sample_rate() + if sample_rate is not None: + span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, sample_rate) + + # inject tags from pin + if self._pin.tags: + span.set_tags(self._pin.tags) + + # propagate distributed tracing headers if available + headers = {} + if config.grpc.distributed_tracing_enabled: + HTTPPropagator.inject(span.context, headers) + + metadata = [] + if client_call_details.metadata is not None: + metadata = list(client_call_details.metadata) + metadata.extend(headers.items()) + + client_call_details = _ClientCallDetails( + client_call_details.method, + client_call_details.timeout, + metadata, + client_call_details.credentials, + ) + + return span, client_call_details + + def intercept_unary_unary(self, continuation, client_call_details, request): + span, client_call_details = self._intercept_client_call( + constants.GRPC_METHOD_KIND_UNARY, + client_call_details, + ) + try: + response = continuation(client_call_details, request) + _handle_response(span, response) + except grpc.RpcError as rpc_error: + # DEV: grpcio<1.18.0 grpc.RpcError is raised rather than returned as response + # https://github.com/grpc/grpc/commit/8199aff7a66460fbc4e9a82ade2e95ef076fd8f9 + # handle as a response + _handle_response(span, rpc_error) + raise + + return response + + def intercept_unary_stream(self, continuation, client_call_details, request): + span, client_call_details = self._intercept_client_call( + constants.GRPC_METHOD_KIND_SERVER_STREAMING, + client_call_details, + ) + response_iterator = continuation(client_call_details, request) + response_iterator = _WrappedResponseCallFuture(response_iterator, span) + return response_iterator + + def intercept_stream_unary(self, continuation, client_call_details, request_iterator): + span, client_call_details = self._intercept_client_call( + constants.GRPC_METHOD_KIND_CLIENT_STREAMING, + client_call_details, + ) + try: + response = continuation(client_call_details, request_iterator) + _handle_response(span, response) + except grpc.RpcError as rpc_error: + # DEV: grpcio<1.18.0 grpc.RpcError is raised rather than returned as response + # https://github.com/grpc/grpc/commit/8199aff7a66460fbc4e9a82ade2e95ef076fd8f9 + # handle as a response + _handle_response(span, rpc_error) + raise + + return response + + def intercept_stream_stream(self, continuation, client_call_details, request_iterator): + span, client_call_details = self._intercept_client_call( + constants.GRPC_METHOD_KIND_BIDI_STREAMING, + client_call_details, + ) + response_iterator = continuation(client_call_details, request_iterator) + response_iterator = _WrappedResponseCallFuture(response_iterator, span) + return response_iterator diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/constants.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/constants.py new file mode 100644 index 000000000..385013ba3 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/constants.py @@ -0,0 +1,24 @@ +import grpc + + +GRPC_PIN_MODULE_SERVER = grpc.Server +GRPC_PIN_MODULE_CLIENT = grpc.Channel +GRPC_METHOD_PATH_KEY = "grpc.method.path" +GRPC_METHOD_PACKAGE_KEY = "grpc.method.package" +GRPC_METHOD_SERVICE_KEY = "grpc.method.service" +GRPC_METHOD_NAME_KEY = "grpc.method.name" +GRPC_METHOD_KIND_KEY = "grpc.method.kind" +GRPC_STATUS_CODE_KEY = "grpc.status.code" +GRPC_REQUEST_METADATA_PREFIX_KEY = "grpc.request.metadata." +GRPC_RESPONSE_METADATA_PREFIX_KEY = "grpc.response.metadata." +GRPC_HOST_KEY = "grpc.host" +GRPC_PORT_KEY = "grpc.port" +GRPC_SPAN_KIND_KEY = "span.kind" +GRPC_SPAN_KIND_VALUE_CLIENT = "client" +GRPC_SPAN_KIND_VALUE_SERVER = "server" +GRPC_METHOD_KIND_UNARY = "unary" +GRPC_METHOD_KIND_CLIENT_STREAMING = "client_streaming" +GRPC_METHOD_KIND_SERVER_STREAMING = "server_streaming" +GRPC_METHOD_KIND_BIDI_STREAMING = "bidi_streaming" +GRPC_SERVICE_SERVER = "grpc-server" +GRPC_SERVICE_CLIENT = "grpc-client" diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/patch.py new file mode 100644 index 000000000..d39519a4f --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/patch.py @@ -0,0 +1,121 @@ +import grpc + +from ddtrace import Pin +from ddtrace import config +from ddtrace.vendor.wrapt import wrap_function_wrapper as _w + +from . import constants +from . import utils +from ...utils.wrappers import unwrap as _u +from .client_interceptor import create_client_interceptor +from .client_interceptor import intercept_channel +from .server_interceptor import create_server_interceptor + + +config._add( + "grpc_server", + dict( + _default_service=constants.GRPC_SERVICE_SERVER, + distributed_tracing_enabled=True, + ), +) + + +# TODO[tbutt]: keeping name for client config unchanged to maintain backwards +# compatibility but should change in future +config._add( + "grpc", + dict( + _default_service=constants.GRPC_SERVICE_CLIENT, + distributed_tracing_enabled=True, + ), +) + + +def patch(): + _patch_client() + _patch_server() + + +def unpatch(): + _unpatch_client() + _unpatch_server() + + +def _patch_client(): + if getattr(constants.GRPC_PIN_MODULE_CLIENT, "__datadog_patch", False): + return + setattr(constants.GRPC_PIN_MODULE_CLIENT, "__datadog_patch", True) + + Pin().onto(constants.GRPC_PIN_MODULE_CLIENT) + + _w("grpc", "insecure_channel", _client_channel_interceptor) + _w("grpc", "secure_channel", _client_channel_interceptor) + _w("grpc", "intercept_channel", intercept_channel) + + +def _unpatch_client(): + if not getattr(constants.GRPC_PIN_MODULE_CLIENT, "__datadog_patch", False): + return + setattr(constants.GRPC_PIN_MODULE_CLIENT, "__datadog_patch", False) + + pin = Pin.get_from(constants.GRPC_PIN_MODULE_CLIENT) + if pin: + pin.remove_from(constants.GRPC_PIN_MODULE_CLIENT) + + _u(grpc, "secure_channel") + _u(grpc, "insecure_channel") + + +def _patch_server(): + if getattr(constants.GRPC_PIN_MODULE_SERVER, "__datadog_patch", False): + return + setattr(constants.GRPC_PIN_MODULE_SERVER, "__datadog_patch", True) + + Pin().onto(constants.GRPC_PIN_MODULE_SERVER) + + _w("grpc", "server", _server_constructor_interceptor) + + +def _unpatch_server(): + if not getattr(constants.GRPC_PIN_MODULE_SERVER, "__datadog_patch", False): + return + setattr(constants.GRPC_PIN_MODULE_SERVER, "__datadog_patch", False) + + pin = Pin.get_from(constants.GRPC_PIN_MODULE_SERVER) + if pin: + pin.remove_from(constants.GRPC_PIN_MODULE_SERVER) + + _u(grpc, "server") + + +def _client_channel_interceptor(wrapped, instance, args, kwargs): + channel = wrapped(*args, **kwargs) + + pin = Pin.get_from(channel) + if not pin or not pin.enabled(): + return channel + + (host, port) = utils._parse_target_from_args(args, kwargs) + + interceptor_function = create_client_interceptor(pin, host, port) + return grpc.intercept_channel(channel, interceptor_function) + + +def _server_constructor_interceptor(wrapped, instance, args, kwargs): + # DEV: we clone the pin on the grpc module and configure it for the server + # interceptor + + pin = Pin.get_from(constants.GRPC_PIN_MODULE_SERVER) + if not pin or not pin.enabled(): + return wrapped(*args, **kwargs) + + interceptor = create_server_interceptor(pin) + + # DEV: Inject our tracing interceptor first in the list of interceptors + if "interceptors" in kwargs: + kwargs["interceptors"] = (interceptor,) + tuple(kwargs["interceptors"]) + else: + kwargs["interceptors"] = (interceptor,) + + return wrapped(*args, **kwargs) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/server_interceptor.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/server_interceptor.py new file mode 100644 index 000000000..368f177c7 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/server_interceptor.py @@ -0,0 +1,124 @@ +import grpc + +from ddtrace import config +from ddtrace.internal.compat import to_unicode +from ddtrace.vendor import wrapt + +from . import constants +from .. import trace_utils +from ...constants import ANALYTICS_SAMPLE_RATE_KEY +from ...constants import ERROR_MSG +from ...constants import ERROR_TYPE +from ...constants import SPAN_MEASURED_KEY +from ...ext import SpanTypes +from .utils import set_grpc_method_meta + + +def create_server_interceptor(pin): + def interceptor_function(continuation, handler_call_details): + if not pin.enabled: + return continuation(handler_call_details) + + rpc_method_handler = continuation(handler_call_details) + + # continuation returns an RpcMethodHandler instance if the RPC is + # considered serviced, or None otherwise + # https://grpc.github.io/grpc/python/grpc.html#grpc.ServerInterceptor.intercept_service + + if rpc_method_handler: + return _TracedRpcMethodHandler(pin, handler_call_details, rpc_method_handler) + + return rpc_method_handler + + return _ServerInterceptor(interceptor_function) + + +def _handle_server_exception(server_context, span): + if server_context is not None and hasattr(server_context, "_state") and server_context._state is not None: + code = to_unicode(server_context._state.code) + details = to_unicode(server_context._state.details) + span.error = 1 + span._set_str_tag(ERROR_MSG, details) + span._set_str_tag(ERROR_TYPE, code) + + +def _wrap_response_iterator(response_iterator, server_context, span): + try: + for response in response_iterator: + yield response + except Exception: + span.set_traceback() + _handle_server_exception(server_context, span) + raise + finally: + span.finish() + + +class _TracedRpcMethodHandler(wrapt.ObjectProxy): + def __init__(self, pin, handler_call_details, wrapped): + super(_TracedRpcMethodHandler, self).__init__(wrapped) + self._pin = pin + self._handler_call_details = handler_call_details + + def _fn(self, method_kind, behavior, args, kwargs): + tracer = self._pin.tracer + headers = dict(self._handler_call_details.invocation_metadata) + + trace_utils.activate_distributed_headers(tracer, int_config=config.grpc_server, request_headers=headers) + + span = tracer.trace( + "grpc", + span_type=SpanTypes.GRPC, + service=trace_utils.int_service(self._pin, config.grpc_server), + resource=self._handler_call_details.method, + ) + span.set_tag(SPAN_MEASURED_KEY) + + set_grpc_method_meta(span, self._handler_call_details.method, method_kind) + span._set_str_tag(constants.GRPC_SPAN_KIND_KEY, constants.GRPC_SPAN_KIND_VALUE_SERVER) + + sample_rate = config.grpc_server.get_analytics_sample_rate() + if sample_rate is not None: + span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, sample_rate) + + # access server context by taking second argument as server context + # if not found, skip using context to tag span with server state information + server_context = args[1] if isinstance(args[1], grpc.ServicerContext) else None + + if self._pin.tags: + span.set_tags(self._pin.tags) + + try: + response_or_iterator = behavior(*args, **kwargs) + + if self.__wrapped__.response_streaming: + response_or_iterator = _wrap_response_iterator(response_or_iterator, server_context, span) + except Exception: + span.set_traceback() + _handle_server_exception(server_context, span) + raise + finally: + if not self.__wrapped__.response_streaming: + span.finish() + + return response_or_iterator + + def unary_unary(self, *args, **kwargs): + return self._fn(constants.GRPC_METHOD_KIND_UNARY, self.__wrapped__.unary_unary, args, kwargs) + + def unary_stream(self, *args, **kwargs): + return self._fn(constants.GRPC_METHOD_KIND_SERVER_STREAMING, self.__wrapped__.unary_stream, args, kwargs) + + def stream_unary(self, *args, **kwargs): + return self._fn(constants.GRPC_METHOD_KIND_CLIENT_STREAMING, self.__wrapped__.stream_unary, args, kwargs) + + def stream_stream(self, *args, **kwargs): + return self._fn(constants.GRPC_METHOD_KIND_BIDI_STREAMING, self.__wrapped__.stream_stream, args, kwargs) + + +class _ServerInterceptor(grpc.ServerInterceptor): + def __init__(self, interceptor_function): + self._fn = interceptor_function + + def intercept_service(self, continuation, handler_call_details): + return self._fn(continuation, handler_call_details) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/utils.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/utils.py new file mode 100644 index 000000000..c5bfa7062 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/grpc/utils.py @@ -0,0 +1,73 @@ +import logging + +from ddtrace.internal.compat import parse + +from . import constants + + +log = logging.getLogger(__name__) + + +def parse_method_path(method_path): + """Returns (package, service, method) tuple from parsing method path""" + # unpack method path based on "/{package}.{service}/{method}" + # first remove leading "/" as unnecessary + package_service, method_name = method_path.lstrip("/").rsplit("/", 1) + + # {package} is optional + package_service = package_service.rsplit(".", 1) + if len(package_service) == 2: + return package_service[0], package_service[1], method_name + + return None, package_service[0], method_name + + +def set_grpc_method_meta(span, method, method_kind): + method_path = method + method_package, method_service, method_name = parse_method_path(method_path) + if method_path is not None: + span._set_str_tag(constants.GRPC_METHOD_PATH_KEY, method_path) + if method_package is not None: + span._set_str_tag(constants.GRPC_METHOD_PACKAGE_KEY, method_package) + if method_service is not None: + span._set_str_tag(constants.GRPC_METHOD_SERVICE_KEY, method_service) + if method_name is not None: + span._set_str_tag(constants.GRPC_METHOD_NAME_KEY, method_name) + if method_kind is not None: + span._set_str_tag(constants.GRPC_METHOD_KIND_KEY, method_kind) + + +def set_grpc_client_meta(span, host, port): + if host: + span._set_str_tag(constants.GRPC_HOST_KEY, host) + if port: + span._set_str_tag(constants.GRPC_PORT_KEY, str(port)) + span._set_str_tag(constants.GRPC_SPAN_KIND_KEY, constants.GRPC_SPAN_KIND_VALUE_CLIENT) + + +def _parse_target_from_args(args, kwargs): + if "target" in kwargs: + target = kwargs["target"] + else: + target = args[0] + + try: + if target is None: + return + + # ensure URI follows RFC 3986 and is preceded by double slash + # https://tools.ietf.org/html/rfc3986#section-3.2 + parsed = parse.urlsplit("//" + target if not target.startswith("//") else target) + port = None + try: + port = parsed.port + except ValueError: + log.warning("Non-integer port in target '%s'", target) + + # an empty hostname in Python 2.7 will be an empty string rather than + # None + hostname = parsed.hostname if parsed.hostname is not None and len(parsed.hostname) > 0 else None + + return hostname, port + except ValueError: + log.warning("Malformed target '%s'.", target) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httplib/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httplib/__init__.py new file mode 100644 index 000000000..ce4fbeb53 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httplib/__init__.py @@ -0,0 +1,65 @@ +""" +Trace the standard library ``httplib``/``http.client`` libraries to trace +HTTP requests. + + +Enabling +~~~~~~~~ + +The httplib integration is disabled by default. It can be enabled when using +:ref:`ddtrace-run` using the ``DD_TRACE_HTTPLIB_ENABLED`` +environment variable:: + + DD_TRACE_HTTPLIB_ENABLED=true ddtrace-run .... + +The integration can also be enabled manually in code with +:ref:`patch_all()`:: + + from ddtrace import patch_all + patch_all(httplib=True) + + +Global Configuration +~~~~~~~~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.httplib['distributed_tracing'] + + Include distributed tracing headers in requests sent from httplib. + + This option can also be set with the ``DD_HTTPLIB_DISTRIBUTED_TRACING`` + environment variable. + + Default: ``True`` + + +Instance Configuration +~~~~~~~~~~~~~~~~~~~~~~ + + +The integration can be configured per instance:: + + from ddtrace import config + + # Disable distributed tracing globally. + config.httplib['distributed_tracing'] = False + + # Change the service distributed tracing option only for this HTTP + # connection. + + # Python 2 + connection = urllib.HTTPConnection('www.datadog.com') + + # Python 3 + connection = http.client.HTTPConnection('www.datadog.com') + + cfg = config.get_from(connection) + cfg['distributed_tracing'] = True + + +:ref:`Headers tracing ` is supported for this integration. +""" +from .patch import patch +from .patch import unpatch + + +__all__ = ["patch", "unpatch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httplib/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httplib/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9a5a9114c3c707aaa842828a4e2706333aebdf20 GIT binary patch literal 1733 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 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httplib/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httplib/patch.py new file mode 100644 index 000000000..cb32f3a0d --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httplib/patch.py @@ -0,0 +1,206 @@ +import sys + +import six + +from ddtrace import config +from ddtrace.vendor import wrapt + +from .. import trace_utils +from ...constants import ANALYTICS_SAMPLE_RATE_KEY +from ...ext import SpanTypes +from ...internal.compat import PY2 +from ...internal.compat import httplib +from ...internal.compat import parse +from ...internal.logger import get_logger +from ...pin import Pin +from ...propagation.http import HTTPPropagator +from ...utils.formats import asbool +from ...utils.formats import get_env +from ...utils.wrappers import unwrap as _u + + +span_name = "httplib.request" if PY2 else "http.client.request" + +log = get_logger(__name__) + + +config._add( + "httplib", + { + "distributed_tracing": asbool(get_env("httplib", "distributed_tracing", default=True)), + }, +) + + +def _wrap_init(func, instance, args, kwargs): + Pin(app="httplib", service=None, _config=config.httplib).onto(instance) + return func(*args, **kwargs) + + +def _wrap_getresponse(func, instance, args, kwargs): + # Use any attached tracer if available, otherwise use the global tracer + pin = Pin.get_from(instance) + if not pin or not pin.enabled(): + return func(*args, **kwargs) + + resp = None + try: + resp = func(*args, **kwargs) + return resp + finally: + try: + # Get the span attached to this instance, if available + span = getattr(instance, "_datadog_span", None) + if span: + if resp: + trace_utils.set_http_meta( + span, config.httplib, status_code=resp.status, response_headers=resp.getheaders() + ) + + span.finish() + delattr(instance, "_datadog_span") + except Exception: + log.debug("error applying request tags", exc_info=True) + + +def _wrap_request(func, instance, args, kwargs): + # Use any attached tracer if available, otherwise use the global tracer + pin = Pin.get_from(instance) + if should_skip_request(pin, instance): + return func(*args, **kwargs) + + cfg = config.get_from(instance) + + try: + # Create a new span and attach to this instance (so we can retrieve/update/close later on the response) + span = pin.tracer.trace(span_name, span_type=SpanTypes.HTTP) + setattr(instance, "_datadog_span", span) + + # propagate distributed tracing headers + if cfg.get("distributed_tracing"): + if len(args) > 3: + headers = args[3] + else: + headers = kwargs.setdefault("headers", {}) + HTTPPropagator.inject(span.context, headers) + except Exception: + log.debug("error configuring request", exc_info=True) + span = getattr(instance, "_datadog_span", None) + if span: + span.finish() + + try: + return func(*args, **kwargs) + except Exception: + span = getattr(instance, "_datadog_span", None) + exc_info = sys.exc_info() + if span: + span.set_exc_info(*exc_info) + span.finish() + six.reraise(*exc_info) + + +def _wrap_putrequest(func, instance, args, kwargs): + # Use any attached tracer if available, otherwise use the global tracer + pin = Pin.get_from(instance) + if should_skip_request(pin, instance): + return func(*args, **kwargs) + + try: + if hasattr(instance, "_datadog_span"): + # Reuse an existing span set in _wrap_request + span = instance._datadog_span + else: + # Create a new span and attach to this instance (so we can retrieve/update/close later on the response) + span = pin.tracer.trace(span_name, span_type=SpanTypes.HTTP) + setattr(instance, "_datadog_span", span) + + method, path = args[:2] + scheme = "https" if isinstance(instance, httplib.HTTPSConnection) else "http" + port = ":{port}".format(port=instance.port) + + if (scheme == "http" and instance.port == 80) or (scheme == "https" and instance.port == 443): + port = "" + url = "{scheme}://{host}{port}{path}".format(scheme=scheme, host=instance.host, port=port, path=path) + + # sanitize url + parsed = parse.urlparse(url) + sanitized_url = parse.urlunparse( + (parsed.scheme, parsed.netloc, parsed.path, parsed.params, None, parsed.fragment) # drop query + ) + trace_utils.set_http_meta(span, config.httplib, method=method, url=sanitized_url, query=parsed.query) + + # set analytics sample rate + span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, config.httplib.get_analytics_sample_rate()) + except Exception: + log.debug("error applying request tags", exc_info=True) + + # Close the span to prevent memory leaks. + span = getattr(instance, "_datadog_span", None) + if span: + span.finish() + + try: + return func(*args, **kwargs) + except Exception: + span = getattr(instance, "_datadog_span", None) + exc_info = sys.exc_info() + if span: + span.set_exc_info(*exc_info) + span.finish() + six.reraise(*exc_info) + + +def _wrap_putheader(func, instance, args, kwargs): + span = getattr(instance, "_datadog_span", None) + if span: + request_headers = {args[0]: args[1]} + trace_utils.set_http_meta(span, config.httplib, request_headers=request_headers) + + return func(*args, **kwargs) + + +def should_skip_request(pin, request): + """Helper to determine if the provided request should be traced""" + if not pin or not pin.enabled(): + return True + + if hasattr(pin.tracer.writer, "agent_url"): + parsed = parse.urlparse(pin.tracer.writer.agent_url) + return request.host == parsed.hostname and request.port == parsed.port + return False + + +def patch(): + """patch the built-in urllib/httplib/httplib.client methods for tracing""" + if getattr(httplib, "__datadog_patch", False): + return + setattr(httplib, "__datadog_patch", True) + + # Patch the desired methods + setattr(httplib.HTTPConnection, "__init__", wrapt.FunctionWrapper(httplib.HTTPConnection.__init__, _wrap_init)) + setattr( + httplib.HTTPConnection, + "getresponse", + wrapt.FunctionWrapper(httplib.HTTPConnection.getresponse, _wrap_getresponse), + ) + setattr(httplib.HTTPConnection, "request", wrapt.FunctionWrapper(httplib.HTTPConnection.request, _wrap_request)) + setattr( + httplib.HTTPConnection, "putrequest", wrapt.FunctionWrapper(httplib.HTTPConnection.putrequest, _wrap_putrequest) + ) + setattr( + httplib.HTTPConnection, "putheader", wrapt.FunctionWrapper(httplib.HTTPConnection.putheader, _wrap_putheader) + ) + + +def unpatch(): + """unpatch any previously patched modules""" + if not getattr(httplib, "__datadog_patch", False): + return + setattr(httplib, "__datadog_patch", False) + + _u(httplib.HTTPConnection, "__init__") + _u(httplib.HTTPConnection, "getresponse") + _u(httplib.HTTPConnection, "request") + _u(httplib.HTTPConnection, "putrequest") + _u(httplib.HTTPConnection, "putheader") diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httpx/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httpx/__init__.py new file mode 100644 index 000000000..c98ca91b7 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httpx/__init__.py @@ -0,0 +1,93 @@ +""" +The httpx__ integration traces all HTTP requests made with the ``httpx`` +library. + +Enabling +~~~~~~~~ + +The ``httpx`` integration is enabled automatically when using +:ref:`ddtrace-run` or :ref:`patch_all()`. + +Alternatively, use :ref:`patch()` to manually enable the integration:: + + from ddtrace import patch + patch(httpx=True) + + # use httpx like usual + + +Global Configuration +~~~~~~~~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.httpx['service'] + + The default service name for ``httpx`` requests. + By default the ``httpx`` integration will not define a service name and inherit + its service name from its parent span. + + If you are making calls to uninstrumented third party applications you can + set this setting, use the ``ddtrace.config.httpx['split_by_domain']`` setting, + or use a ``Pin`` to override an individual connection's settings (see example + below for ``Pin`` usage). + + This option can also be set with the ``DD_HTTPX_SERVICE`` environment + variable. + + Default: ``None`` + + +.. py:data:: ddtrace.config.httpx['distributed_tracing'] + + Whether or not to inject distributed tracing headers into requests. + + Default: ``True`` + + +.. py:data:: ddtrace.config.httpx['split_by_domain'] + + Whether or not to use the domain name of requests as the service name. This + setting can be overridden with session overrides (described in the Instance + Configuration section). + + This setting takes precedence over ``ddtrace.config.httpx['service']`` + + Default: ``False`` + + +Instance Configuration +~~~~~~~~~~~~~~~~~~~~~~ + +To configure particular ``httpx`` client instances use the :ref:`Pin` API:: + + import httpx + from ddtrace import Pin + + client = httpx.Client() + # Override service name for this instance + Pin.override(client, service="custom-http-service") + + async_client = httpx.AsyncClient( + # Override service name for this instance + Pin.override(async_client, service="custom-async-http-service") + + +:ref:`Headers tracing ` is supported for this integration. + +:ref:`HTTP Tagging ` is supported for this integration. + +.. __: https://www.python-httpx.org/ +""" +from ...utils.importlib import require_modules + + +required_modules = ["httpx"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + from .patch import unpatch + + __all__ = [ + "patch", + "unpatch", + ] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httpx/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httpx/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2b20158b885800c0ec234ff51591b8e7d2737637 GIT binary patch literal 2641 zcmbtW-EJF26ka=alCD}6@dTsxA~9;bEtE>J(umL$nwz$?hE_(^db~5XCtc4jGqZNw z3vQ6OMZ5s_yatcaTdw*FTp+$PyX!g)NRe2{>+#H--|su;_}gpO!T`^`-+twPTnU0d z>Bstmz2FOc>@&PXFbG6LG}FecIcPQlu`y{3He^$7{M7j6lR-in>v@D#W7Rb$%)}kWtus| z6KNSwQ}(Fef69#fp^(;DHseCFg>qBoAUYg+;o&e$)yQzO?1te(%}1%ylkmst3B8ro z)pK@~Ws>eo!Fb`a8LTHTx@3!~)U2?ir)T82Hx$DAv}X!^P_3~%9J0)?@TfI{qkY1Wv15)LxyK;vKg!VU}~0U z#xZZypZe9V&&~b5DP*S zjn|+6BTiUCr_f4UXNnmlz!^7HMvwxxWIWGPkca4Ny?nwoX|vMNJ+kf`l$H3GVO0Qr zm_I0T@n{(f5K`%#SHs~-m^319L?MpsJyjZqa4efkV-#wGYeX&7TnRwS0F0Ii`Lwfg zz_MK{C6i}-mZy>wj%1oGDm+S!h2<03Dbvv>=d;|`C1OHR+6*E-u(e7&K8~qWU&hZL zKKt(E!9(~Z^<0@uQ?kg+oEt^8R;oQNInhHDk25V%!iM0+un z$f}|A5G#-rrB4w&TNCk(g%F)eG!kQpP}!=xTyTtP=dzC34^i`6y66-^N9dQ}mZgx5 zS3WQfqTzU*t6fGIRAeF!fsmRYs8AQ^RUQc|t)=!^vjI>GX%plNWL0RZP<#RcbDenL z&lNmOdZw(yUU8N={u=E#H!_hhmXwx(+(%O$pbRFtD{fx-$ z+mrMar;;M&>m4LX71c2nD8*-ekx;~Cdj_!&@IjY7dU{f~(W-O$#zmg~YhQ(w4}(<} z?w2F0`@kFA?RdeP>`7gM>(KeC^5tLq_*kX zxn1fc{=ktV+N`|!zs{^1xag8Uz2I0iA|F*XRh1<>@BvhnwpZ;U|I`bMoJJN{NmHQ8 z&uaXm4ol4Z?C61!LLX<9fiZM+_Lw%=~W=+{Xc53UzZrM6pA zi~g51abxgNrChAEU!N&dtDe*w@70kmOq@9WJuvu}RKEUNM7dFO?qq~U8jZ7*8d&t^ zPP9MLi__cpPCqqwKGXT__-vy0_ut&TP0u8c`cba(Jx{+h-J8G3_m|6=jH=RIeWSXl tu|_`TD2|oJ5Q>qmx7!D0LJlbr_G7$SPp)okHp51%akX)EquFTw{SO_Ccenrm literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httpx/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httpx/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d090ef8243b457a813056bab89fc6498404f0dc3 GIT binary patch literal 3717 zcmaJ^&2JmW72j_zKlN?Nb}ZA5Vw-6r+K%JYZkk$F#fWx%0Ear`H=P=-uRiSC`@5$pg5yebyUb&pmlUdSE)}2Z#ahJjlk?!jwN|B z$aQSTmb?W%@8l()3ksd0QWVLvd;6Y6${?l>j|H^f_sft*mV8S+Y5Xqzw}DDvCj`%4Ig4T_Jw;OMAz;3!b3-X zr|X5g{jMKF(6?J5n#$(x?)Em1x?a;05l7E#6Vp82>~;K5xQAZQqs1&Q-ix9DT#od9 zcu1YS@Fn-U;1;NL_j_SOw4%@@sq52f27MFl{QamGvIp6Krz3r8()M1ZbElkZ7n5?# z-}icfNT=d%&=T%m-(^w9YlRPL8ef3H`65UIjy-uyaP+R@jURv{N?ZA@F;EBEKpz;1 z%G5|G)IKEJYmcTiN(0)ofB{8TKdZG*oVB=`LP{p(92RK+&L{;E; z=yOpO2Yyu^WA(}7N7b&!MO@wIQK!1s7k*45y(&EJi@7BAm?nkXK7G4m1ba>Rg z)9-iuI%DKkZ(1CZ9Ih@8SjUGO2dmwFT6D2NS41xMyn=1SB@o4|s3p}_=kfAOkQ;Qa z@ne9Q0P{Trp|sVu#vjAPYTIDyP~%nN+Ge7-R(Mn7q+|~Po#gWTQKB(T zb1St%Uj$KuFJZ56MrWoM`))Ic_PoH&VtAg@srWn*^29WE;KiN*j_0ZCvh2zNQi9Ac zQ2#QKb0AKRLR5H7Zc$f`2wBpxT4CF7h%`s`N8Z4~zDLM9XJvGTUeFh+>W~COA)DrVWjOA=G1-?X@~E6O*qb=CS@- ziEkkpVa%V@hp5q;@Xo}fC_&P)5>w>b_D~b~b^#u(|9Nv@iDF_U#*iwSGRPgRyghr6 zOL9+@A1Y53P5DImjq+Ie8&(u0nv>*)xCcB_o@Sb7SYb!`(cI#ct)yL!N|Xs!Gt(mr zQ}jD|avoQpvee4FOk?ScsZG90sW8oDMJlyqdf}-#YRMcY;2}3k4XQ}e&7}EicA6WN z6PDUCUxf|iL=>nvwO+iGt&aVqdbbry08@jodl$F4tSB+pi~C^%D28ko-;2+HD7gwe zyaXR#RttbmQ>WE=jsFUoQ{j`o{ugM4a;!?gjKtt)6606{VAZk7VOaeHyk=rdfHU3! z?tTWGIRS2YyvqN9%sd@3ejQr_6odk&pyoG;NMt@H?iP_#jL$zI1%-HKdw!evPl^13 z$Q>fTByyLCjF-=ddnc*PPOLpK_6rQlj7_F4XqAbv3uNrip>N=wJmho~JJQ&{10#@D zWTfbPnmkizQr5|91xh%oh#}00WI>W1Eo^2o3;7<2={Qg0s77SGMLexm%WWaJnI}9J zHL1on4j$|xo5D|IhDQm|ajU5nd5Zg#ayUt;=DtjeYjocApoP1Wbf4791UjAs z#4(W5qnbK0<2!aU<#+#pvGFvBf(&eEC1l=_e{xDUZyAXOO^ieky)jaJ(n#9GEC$ob z$E3lKL`MEbH=Hg0Ys`PZ?}64VzJ_L0+U|qH>3aMN>e?XkX4{gh(uiCyl)L{HLQJ7N z-hD~sn~-WBBK-S^(xzM%7oOIuQB~BTb#mF=L*@f#CX-fC0g?Pe5zG1}3#D`Zgp#f& zt1_FoJ<$r{Q5BWGP08OWj>n>ma@y6K9!eM#BF@ryOgFm-;|{w@iEdPl%VYKE0~AKi z!drc-`w{PW*kc7cK1Bo0ygeYh5yzZh`SFNDxLCwjX^7x6q$ihm%Dd0EH{A8j zjrFe{Zhgt|=T6~sq%d5Mu~8YDgsOBl8AH-Xq`^-`$^NVpv0mVRPT>_3wDzRfqvW6d c(=F=PkQlXTb^0gcCnIl{?FGAHFXA`#KW%bo@c;k- literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httpx/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httpx/patch.py new file mode 100644 index 000000000..6518fa974 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/httpx/patch.py @@ -0,0 +1,153 @@ +import typing + +import httpx +from six import ensure_binary +from six import ensure_text + +from ddtrace import config +from ddtrace.constants import ANALYTICS_SAMPLE_RATE_KEY +from ddtrace.constants import SPAN_MEASURED_KEY +from ddtrace.contrib.trace_utils import distributed_tracing_enabled +from ddtrace.contrib.trace_utils import ext_service +from ddtrace.contrib.trace_utils import set_http_meta +from ddtrace.ext import SpanTypes +from ddtrace.pin import Pin +from ddtrace.propagation.http import HTTPPropagator +from ddtrace.utils import get_argument_value +from ddtrace.utils.formats import asbool +from ddtrace.utils.formats import get_env +from ddtrace.utils.wrappers import unwrap as _u +from ddtrace.vendor.wrapt import wrap_function_wrapper as _w + + +if typing.TYPE_CHECKING: + from ddtrace import Span + from ddtrace.vendor.wrapt import BoundFunctionWrapper + +config._add( + "httpx", + { + "distributed_tracing": asbool(get_env("httpx", "distributed_tracing", default=True)), + "split_by_domain": asbool(get_env("httpx", "split_by_domain", default=False)), + }, +) + + +def _url_to_str(url): + # type: (httpx.URL) -> str + """ + Helper to convert the httpx.URL parts from bytes to a str + """ + scheme, host, port, raw_path = url.raw + url = scheme + b"://" + host + if port is not None: + url += b":" + ensure_binary(str(port)) + url += raw_path + return ensure_text(url) + + +def _init_span(span, request): + # type: (Span, httpx.Request) -> None + if config.httpx.split_by_domain: + if hasattr(request.url, "netloc"): + span.service = request.url.netloc + else: + service = ensure_binary(request.url.host) + if request.url.port: + service += b":" + ensure_binary(str(request.url.port)) + span.service = service + + span.set_tag(SPAN_MEASURED_KEY) + + if distributed_tracing_enabled(config.httpx): + HTTPPropagator.inject(span.context, request.headers) + + sample_rate = config.httpx.get_analytics_sample_rate(use_global_config=True) + if sample_rate is not None: + span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, sample_rate) + + +def _set_span_meta(span, request, response): + # type: (Span, httpx.Request, httpx.Response) -> None + set_http_meta( + span, + config.httpx, + method=request.method, + url=_url_to_str(request.url), + status_code=response.status_code if response else None, + query=request.url.query, + request_headers=request.headers, + response_headers=response.headers if response else None, + ) + + +async def _wrapped_async_send( + wrapped, # type: BoundFunctionWrapper + instance, # type: httpx.AsyncClient + args, # type: typing.Tuple[httpx.Request], + kwargs, # type: typing.Dict[typing.Str, typing.Any] +): + # type: (...) -> typing.Coroutine[None, None, httpx.Response] + req = get_argument_value(args, kwargs, 0, "request") + + pin = Pin.get_from(instance) + if not pin or not pin.enabled(): + return await wrapped(*args, **kwargs) + + with pin.tracer.trace("http.request", service=ext_service(pin, config.httpx), span_type=SpanTypes.HTTP) as span: + _init_span(span, req) + resp = None + try: + resp = await wrapped(*args, **kwargs) + return resp + finally: + _set_span_meta(span, req, resp) + + +def _wrapped_sync_send( + wrapped, # type: BoundFunctionWrapper + instance, # type: httpx.AsyncClient + args, # type: typing.Tuple[httpx.Request] + kwargs, # type: typing.Dict[typing.Str, typing.Any] +): + # type: (...) -> httpx.Response + pin = Pin.get_from(instance) + if not pin or not pin.enabled(): + return wrapped(*args, **kwargs) + + req = get_argument_value(args, kwargs, 0, "request") + + with pin.tracer.trace("http.request", service=ext_service(pin, config.httpx), span_type=SpanTypes.HTTP) as span: + _init_span(span, req) + resp = None + try: + resp = wrapped(*args, **kwargs) + return resp + finally: + _set_span_meta(span, req, resp) + + +def patch(): + # type: () -> None + if getattr(httpx, "_datadog_patch", False): + return + + setattr(httpx, "_datadog_patch", True) + + _w(httpx.AsyncClient, "send", _wrapped_async_send) + _w(httpx.Client, "send", _wrapped_sync_send) + + pin = Pin(app="httpx") + pin.onto(httpx.AsyncClient) + pin.onto(httpx.Client) + + +def unpatch(): + # type: () -> None + if not getattr(httpx, "_datadog_patch", False): + return + + setattr(httpx, "_datadog_patch", False) + + _u(httpx.AsyncClient, "send") + _u(httpx.Client, "send") diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/jinja2/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/jinja2/__init__.py new file mode 100644 index 000000000..e070711c2 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/jinja2/__init__.py @@ -0,0 +1,43 @@ +""" +The ``jinja2`` integration traces templates loading, compilation and rendering. +Auto instrumentation is available using the ``patch``. The following is an example:: + + from ddtrace import patch + from jinja2 import Environment, FileSystemLoader + + patch(jinja2=True) + + env = Environment( + loader=FileSystemLoader("templates") + ) + template = env.get_template('mytemplate.html') + + +The library can be configured globally and per instance, using the Configuration API:: + + from ddtrace import config + + # Change service name globally + config.jinja2['service_name'] = 'jinja-templates' + + # change the service name only for this environment + cfg = config.get_from(env) + cfg['service_name'] = 'jinja-templates' + +By default, the service name is set to None, so it is inherited from the parent span. +If there is no parent span and the service name is not overridden the agent will drop the traces. +""" +from ...utils.importlib import require_modules + + +required_modules = ["jinja2"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + from .patch import unpatch + + __all__ = [ + "patch", + "unpatch", + ] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/jinja2/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/jinja2/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..228a95f5e19db6f481a28e3a80131d8e0364e18e GIT binary patch literal 1389 zcma)6TWcIQ6xPgM>(>*4-z+c}adC{MsurN!a@yA<6Lq=yZA_)Cs#Yol>D3RE9aB6?KM{V5x(; zk<8&rYQ}}C_Gqc=M&O=DF~z9?#lfH{O&+#RBa3yWtwFhf5tgzg!xJ+Jw6zGNuJhY4 zS1zX0l=^GTwUqkQ8$Di8IAiD;9y~}AieqkcO*!vwMMd3c<7jvOqcw!FF@CLhr>QN7x%(}k<$2)%a+kz!_;Sd;H?NR%?bM6st6+?%pl&&@{@)!Q zttf{%YbBP#Kdyj&EjWsW`%EiT(qaWWUxY#}u&f>G-W8ue*f1=1M{UDYnmnC*G6No~ z^i^AzpMP_tw4-_n#t6;?{ zVaULTDA)&GB_nXFRo&9yn?lS0r)QJxi>;#Ix-5!(w{=3=G_;Q|vltQiCu??oNxxeQ z>-%)G@sZARVFeb&FT`wnAAb7%ENhHd;wzCYnaQw|u}5rnemgsysne5#`zLqI?YpWu zD9$Q%czAyAz>liQjBUj}$Db}pxAFi0 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/jinja2/__pycache__/constants.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/jinja2/__pycache__/constants.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..41dbe1e634fc51a03779df7f69bac90faf7ce701 GIT binary patch literal 246 zcmWIL<>g`k0<(D&6P1DVV-N=!FakLaKwQiLBvKfn7*ZI688n%yIBarLbMuQT?ff(u zqeNX?-5f)GLgGVQeFJ?T6^Z4nc1D$?|kPQ->p=N1g>k(|8$>~3Hb+u z(aVJ3I^4IT4CpNZ0@F67Z}?VFunW>R{bEqEOVYRe za!|1=(l3BtwX4!E`eVVkJudx{KM_pYQ{b0b1>QMrPgCM42Q#e7#=LQ~`$~r%;ts~y z#Bao&Ws__Q+EZ+r%|JWHX4xrd=h+;ahjxL{4N_Zp1>Zr|YBV)kQMlD#J+d?Z)cH-8+pN&M%Go&{^1s-LSbEdkF;UdMAXYvdY`N zzzao#^N2&oWZM&tn|C_fuHW;Z*Th~g+y>X^g}B%&nyoZ*qj7Wf?yaWNY}{VIwc2br zYpb^#snP9(UH4L2PCUNdX?afQ23`w4WAxG>_zCVL1S5!`j51|k<>QQO5TVFe-B&3Q zs?fTWY4b$3`cvgV7qn}1%|i;K7R)q6LC!?q9PuqtJnAoq68iY|T5&BMa~$Rhmql$S zc17#a%_bj%jntIX%(tDIPKJ2@G<u)eQ1K?M=oANnyt2C+cy-1EcU#9T;4OIP zVLc;IT_$ER-c5Ds&BG3o1u%q`6^)juLM^JjF;wGUO)EoOru+NAiW7}eqnLf_K|8de#j&|0FV_+PV zL>a5Ezc74mlf0;I5`|nPzmhxTFQ~GCJ}{gL&SmOAN8!6iHuLd&AiYqBdVhHn^yKV= z;g{z{b-woCY{W;phls*Jzml;dpZb#8g#@(C5g=}_r}MF4)kx`Ct5rGTkV0aFv&Ex; z&tPbJq5IJHSgN5w@e1}Cl8`*rvvvadxFMe%YMd-QN4RaS%YN=9QIEI0mu6~aO3YGA zGCgd0sRnYIq{ibN*{X3TOe-fgmuk5Ckz`|@`5c%_a3@F(qF{E5N|DadB9MMTF{qsJ z;Y`1Pr?O1vd7gzlE0E{2!+dR!{-jVb=TdjIJ$;jea~f#u<0Ai=68s?t?vqftM{*{p zM@-Ow3D@?FkLJGw`7bm5J^4o+Hr6=fxV6ava0$9c8WQci6%KUA_KaXOC{x8byo z_##%Q;3~}Lr@*8IIC9`n3PqBYv+6w&vf#5lO4-nRqT?qS-;jBBMRqK1MLclfLHt8% zSF^rM0eHfZA^dYRNKspJT*l<-#8IPV`EsO|-22FPOO8sdx4sV|*7Lm|FnbBo+d?6b bVlWhZXf?n>QHb@Q@Yft%wo;u{N6cwmf&#Lc#44hpRN9g+zq`C31sFp!F0&{M zudhpL(>$RJA!l7-SYLqZL) zIXAef1S6@EGr?0l%T?M#V7chl7 z2Vg!1mky1%Vlq?O`_{M{iMaCW zJV4SZqPhsq3v``-skzeJE<@v@I`+sbnBgh1;B1*nJ?FkGl7og(m|I(#Q5-Yoks=M0 zp)H!4h^rW6Y^)0wQ#PkE1(sCU8HjRQh$pLnhpT{L_y&nol_fq!SI+;GI7A|;#2%n6 z5=HAGNCpy+a-?Fqe1L(1XSqc-3Dc@9frSpjg>&UCa^*;IgrJF!U0X%rXcUG7pP5z# zX-Xx$@PfPGa#;7yn>4$g{o3gXIiGpSd68jyORz)VjFieod3Cck_2I$1N8cZguXF{2 z72=iGpwn_Ad+Iq^>yR**(Mq7?z*;QDcsP`+B;#6f(FJ-{PpvAiqo0vI4j{N)i z@yWC32tUW8r>DSpmKU5U0_8xHAEMUWHWBUO|2C1ML#y@@V`gz^m*9K74r5ggO z85&ERX_hi-DRx|VmHS(Xqo^KLz9iIQq~@SCXV6~}fY$}|oTo7Ec_kepTLv$(?hV5C zTR55rt^0j9jkt!SKu!giN!Od+?fai-Jx|}aYWuENrtaYCP@NRJYM$@==4%*6{lUH^z0oiRY0V3{K-D`76*n=;vSmEMgoJbF4U?QytHgV4zKW zcO0Kh<>Kwp>Dv>1{D~}&lG{w4o!xzU*T(0HJd$rbq@BncH3dA)7oqQ3uUWBV*mgE literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/kombu/__pycache__/constants.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/kombu/__pycache__/constants.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c72844b69b5c33b4f5923537c8397af75260b3b6 GIT binary patch literal 236 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 C73GEi literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/logging/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/logging/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d5373fea93db7ee0bad87ea8bfeaac925583e795 GIT binary patch literal 2928 zcmZ`*&2k&Z5#E{o1qebQMT!(9Ih(j*8%ab+wxfiJ%VkA^ER|`9MZmJXhb$MEAqW`Q zMP>(JzyeRum8r@Lr2KQz3;37^nNzBK@+&x1vAbuNBxJ`kwLLxE^V|J(&wjMLTr{BE z``16C|5Oa)8|*9=2RiS;mwpX|8H@~!AZtlLbe{&4QDzRzp%qxf0NxtdLnm-GZx6g- zAt(ax1WU{fO3VwESs^I1VsM2m1r=5bRyggietwlL^EF0T`HXx)iNQ&Kon2v-GxEN{ zR@mxugRSywb8}9D4R(uNWoyq#u*ufhwdY1~oo%p9Xt&sPwgv4Cc7xr7_9i1uqx#CX zaCW2dtqHVB)M}T@FcRJIkjFBdM1wI0sWXm`MKl6#9oz759Cu_di9_s-xHws2Y+H(` z!^0jsDMBY5MKSUY@I0Pq*@TM}CS`gqCcO?<0wJ4m|SUfg{FJLy$K3%0V;K_b%* z9&fSWLEnQfeGG&fXy|})vu|;mkuwrF3%sj&PsHw%{t7d^f3$L~jyFUYkUsc9;wp7_D}4x3<%ckNbD-_1_eC--<_f z!oPRpot=}n?_lYUYOUI+H@e-6QyC2gwgiir1UiDXsFI|O9s1$hg;2k@o80y2X+(&&>@L(&<<0Hbp=-C|}FG@qMa z7+_rMLn#+# zqMd_MJ~hM{q5!%}1Bx3sbR|R>FAn=Pi1pnJY7xgO5sQjfK-R&#oEH}u8$Nxve*-f! zW^`u$gPdB@m|2oY+6SPGwiub4#LzNZ%bd~ng3Q%2Z}gX3R)D#*U+gb2V{V?>vNXe4 z%X2D@X7;HQS7cewxYDmMdPYv2nIpc`tFOq_nbW^Izua$4OV%?;m zb(Fp|xmn+T_T)ibY~VJxP+fo(0EDqjgVOHP_rvDi{YLmf{Zm!!r5c`bhX+;@J%{!e z9un=}wZ-jP`}ZHz!zT~LHPo-+$}ew>%Yu3xHP;qrKD)pBQ9TUm`%kN`vM^IA?|frr zL265t^VxCM3LWjivX0V2kqko!3}tCtUBqMtsparE97dnM{&;pD5L4`mONrhjmdC((lgKYJGZUaT|Q z-c%kXvEPI!V3A<{Zm%sOG4-i19@f$cpv@t)YtDSyoh?ec9e4e z5VB+fiwrp|&R})Pz$~}rw%*5htP;1{TBw+1worP_^{~?S?;qM7jD`Z^=i!wO3 zL@HGL31mO-)K5UW;M7*`P8(IF%ewYh_6DgAyr8W4?fl0o6$oQt&##$iTDE&9iTck zF(R|r)KQ8-8*KbEH`YJgYkewyiCQa+7*iJOb)+JY7?U=_GmCzmo-u`G9!78JEBs$y rd+%g1@GfSW6m9EKtok>?w7~Hfl{$oC{oeYY5Z3VlZG z=mYA<^r44PJXvs@Wr}_&vJ4Atd2Xl`g~ry<8LzPAs+811WVmMB2%Qh&*R?GGTv<~q zuI;WsRG2QQkaQ|Jt}6gy+mM&k&K6lVz^>Q4kg~XONr%^%uP8L&mzQyj!RDq=n6aig z7OE_a#ZCQv*3CLNjCH_F_G9i?nu=2g98XNm`@36<$M}FdoeRk|ReXFR;90A|!YX+J zz}o=i?t9yw;a_WEI8gtDu$d~^ zoF}8HzPTP=Tt6}APjxvYt6Yypn`c93d6`U-Qj}+IbHI7F+?1pBTJdBr_}zb2XlukY paZgSN5n5P62IYF&d9jn@rPHB$1TO4$gO(qAq1W}gf$#Z${{iT+)~Wyi literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mako/__pycache__/constants.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mako/__pycache__/constants.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ba4a2b4a7662a945b0bc4368e2a13e63c38fa90d GIT binary patch literal 244 zcmWIL<>g`k0<(D&6P1DVV-N=!FakLaKwQiLBvKfn7*ZI688n%yIBarLbMuQT?ff(u zqeNX?-5f)GLgGVQeFJ!VcMHPRwgiB{Pv5xWfAf>DvLt_ zWfx}pD+opy4Tz1a63|ew6^d8|tlFx?TA+oxtpnDX5g4Iqn-ZIW6_)Ih#8yxaD|SWV zQcw+Rc1_}PP!Ah+Q{qan6fWBSvXkOX(hJ7zg0#|sTS_{}1#L>_n-Kk&-M@uJ1dp+l{yLWPW+}(G+?S2oNrIW;s z`d5jUf}$Sz5$+8q(ZGq^5HB<)(Is~iKx?=*yd#5k^PQQD2 z^m4D?b$WY;-2pyxacH1?4Kw`#gdl=4%9NSPn~akso_g1vZxjFKLD+K2a@EX5eXpn=1BO|;C z!q&h(<`}J9aW3sL$+q)$6z%LxKi|eNN;>^c;wR7iC>3rHJiC}CJ6Bhs*I`U>ckr;{ySabfL8>~E zxOQ@t>k{EZ(>e%3tBMA5L#fg#j%r#}#2@WJUp3MQvPX>O1x4 z-D@K=ZW^MAWis8If8~t)UOyuW`GR~$j>#WT8kwFM&Jx-(Ez@s-)mP-~{`^(Ju-?l{ z14t70Jn)>{8v21(^aHz;Lc?$bbd2dTCP;hEV$m>`IVQDjLBcyj9*4FGe+ZYbyzHkk z^P+*rvC-z{Yd4ieQ38!WMIob9!wU?Vl04Uo71NF*z0lz9`-_cbLxA91DOhvwe0x6XxDsxu8cEMBGSY*Ci eUxqQ81m0KJ)TPfBcnsdkBF93PKh>&P<$nR^$j(gw literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mako/constants.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mako/constants.py new file mode 100644 index 000000000..1bda6e1ca --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mako/constants.py @@ -0,0 +1 @@ +DEFAULT_TEMPLATE_NAME = "" diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mako/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mako/patch.py new file mode 100644 index 000000000..924c6881e --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mako/patch.py @@ -0,0 +1,59 @@ +import mako +from mako.template import DefTemplate +from mako.template import Template + +from ddtrace import config + +from ...constants import SPAN_MEASURED_KEY +from ...ext import SpanTypes +from ...pin import Pin +from ...utils.importlib import func_name +from ...utils.wrappers import unwrap as _u +from ...vendor.wrapt import wrap_function_wrapper as _w +from .constants import DEFAULT_TEMPLATE_NAME + + +def patch(): + if getattr(mako, "__datadog_patch", False): + # already patched + return + setattr(mako, "__datadog_patch", True) + + Pin(service=config.service or "mako", app="mako").onto(Template) + + _w(mako, "template.Template.render", _wrap_render) + _w(mako, "template.Template.render_unicode", _wrap_render) + _w(mako, "template.Template.render_context", _wrap_render) + + +def unpatch(): + if not getattr(mako, "__datadog_patch", False): + return + setattr(mako, "__datadog_patch", False) + + _u(mako.template.Template, "render") + _u(mako.template.Template, "render_unicode") + _u(mako.template.Template, "render_context") + + +def _wrap_render(wrapped, instance, args, kwargs): + pin = Pin.get_from(instance) + if not pin or not pin.enabled(): + return wrapped(*args, **kwargs) + + # Determine the resource and `mako.template_name` tag value + # DefTemplate is a wrapper around a callable from another template, it does not have a filename + # https://github.com/sqlalchemy/mako/blob/c2c690ac9add584f2216dc655cdf8215b24ef03c/mako/template.py#L603-L622 + if isinstance(instance, DefTemplate) and hasattr(instance, "callable_"): + template_name = func_name(instance.callable_) + else: + template_name = getattr(instance, "filename", None) + template_name = template_name or DEFAULT_TEMPLATE_NAME + + with pin.tracer.trace(func_name(wrapped), pin.service, span_type=SpanTypes.TEMPLATE) as span: + span.set_tag(SPAN_MEASURED_KEY) + try: + return wrapped(*args, **kwargs) + finally: + span.resource = template_name + span.set_tag("mako.template_name", template_name) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mariadb/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mariadb/__init__.py new file mode 100644 index 000000000..f1803653d --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mariadb/__init__.py @@ -0,0 +1,65 @@ +""" +The MariaDB integration instruments the +`MariaDB library `_ to trace queries. + + +Enabling +~~~~~~~~ + +The MariaDB integration is enabled automatically when using +:ref:`ddtrace-run` or :ref:`patch_all()`. + +Or use :ref:`patch()` to manually enable the integration:: + + from ddtrace import patch + patch(mariadb=True) + + +Global Configuration +~~~~~~~~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.mariadb["service"] + + The service name reported by default for MariaDB spans. + + This option can also be set with the ``DD_MARIADB_SERVICE`` environment + variable. + + Default: ``"mariadb"`` + + +Instance Configuration +~~~~~~~~~~~~~~~~~~~~~~ + +To configure the mariadb integration on an per-connection basis use the +``Pin`` API:: + + from ddtrace import Pin + from ddtrace import patch + + # Make sure to patch before importing mariadb + patch(mariadb=True) + + import mariadb.connector + + # This will report a span with the default settings + conn = mariadb.connector.connect(user="alice", password="b0b", host="localhost", port=3306, database="test") + + # Use a pin to override the service name for this connection. + Pin.override(conn, service="mariadb-users") + + cursor = conn.cursor() + cursor.execute("SELECT 6*7 AS the_answer;") + +""" +from ...utils.importlib import require_modules + + +required_modules = ["mariadb"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + from .patch import unpatch + + __all__ = ["patch", "unpatch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mariadb/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mariadb/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ad9f06bea38718a629713de29491482df98da1d0 GIT binary patch literal 1810 zcmah~-EP}96qf(uR7jUS0^!`)MJ&0?<{@4jERBsKz`AvbyA2)+i4tj>okXf6W!Jgb z%^qOb3+#Tcl-phP6?U~lN=iInTM1Y`G7rCV_$LGp?bj#sCT*$-X_8jbN?Y?5%s0s%#C!8S zh}&d;S)aGRcRq8zb*`Ns9P;SK>ACoM%W>SZNiDI?4WO|iH9uqsWoYPnf+0V9mCAgm}yrkYr zr^$I3AT5xVIHu@rP9>wtb6t1Jag;E=bbl^~``_IvL`^m&2Qt$;^<^{|5yj1*{ zZ`bualx;@@YdjiNWxUuf^h($tJBrFRi)rVzZONFR`~`8GQY5MA3fPRch|mSj6O9(Y zP-Oy@VQw^n-ON@@A(2_r#F!(TD1jn_r_ly%U|9&m$s~AjaeZ|$c^2GEufMyxoQ5H2 zwq{ar<1z!aHckQ&3*bpncZ9;QQ}*2nL*U{Htcf{z<-=?kK4tBuf{NbDlJsz8mWtuR=Dk`P-w%Fq?8q5Wb}Jbr&1Us(@<5}as~#Cs}3Ge ztEdaS$Z-cJ#zx1m0i`xVl5r;*MDV^6N{>5k7_9dVxTJfvyF*Fe z=1kHc6(monnzu_;-q(tRmQ@il+c~$PY{G=evCCV$LHCJvO zGUgwb@Z=umi+Ye_S1nG%gmqo8O!?SPXRFd>)Y&fyG^NI-eULPz`2$zs&0Kh k2pDHN2)t~2w|`cUa&D+lAHkzJJZS9I-CDDDP&=sq4R0k&GXMYp literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mariadb/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mariadb/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2e49f9b4f5c6602ae0ae9bf0c02244460f4cf2f4 GIT binary patch literal 1523 zcmZWo%Wfk@6s_v6e)yF*4+jk(76^^hi*aWFjT8wXMxz8act(k5Q=6vURd(9+OVwp( z5_MK#8zg=JB}=w^PAy@@FCaAHR@sS+D68&0_4K)?PQUH-+6dZs zF}Rg=^B(OP+|IW0ZMuySx9|F_!*_U_cUXsYS&wbq+BX>R_N{fV8T8rCPl)caUA6~# zkL|Mo&R&&8!4sL5deveRy|v&{Z;k6o zS!RH|8J7_+mU?ngT!^>=q-OH)Duf7)(bV4W~3jR4s==3bWs>hB0Z|OP=2&N z{u>f|u+N0QmtiH+Wh{BPj72ytGsZ<7UOf+wCdI}4@WuRd@%)RTI*i`Uild{eFAsH_ zRpB_S(&|}S)H2SpXUnVV=<+h>A!A0v#|(cdMLG%B5{IUqVRfk-gFXNrwGRQ|E+){$ z0Pz3h1YfV@dT9b^Vu0Esfkkb2Zbf7~s{>2fRaz)Jna+Za(8bWs_~q%h$K&X`*JtC$!JF5o zW1&5($lJ5yQ$Qf&f<&xJA=WBgSR%>ilF932kWWf z+HXTPiL2C*r=5p2{!W@AS3*1I%i8MYoo-x!n3OAlJPV>LYOSZ`P*BJh;*4FkdZMFaWY=SNL^|AGy LyeBQ+^WA>|(f4L- literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mariadb/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mariadb/patch.py new file mode 100644 index 000000000..4b90cc807 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mariadb/patch.py @@ -0,0 +1,49 @@ +import mariadb + +from ddtrace import Pin +from ddtrace import config +from ddtrace.contrib.dbapi import TracedConnection +from ddtrace.ext import db +from ddtrace.ext import net +from ddtrace.utils.formats import asbool +from ddtrace.utils.formats import get_env +from ddtrace.utils.wrappers import unwrap +from ddtrace.vendor import wrapt + + +config._add( + "mariadb", + dict( + trace_fetch_methods=asbool(get_env("mariadb", "trace_fetch_methods", default=False)), + _default_service="mariadb", + ), +) + + +def patch(): + if getattr(mariadb, "_datadog_patch", False): + return + setattr(mariadb, "_datadog_patch", True) + wrapt.wrap_function_wrapper("mariadb", "connect", _connect) + + +def unpatch(): + if getattr(mariadb, "_datadog_patch", False): + setattr(mariadb, "_datadog_patch", False) + unwrap(mariadb, "connect") + + +def _connect(func, instance, args, kwargs): + conn = func(*args, **kwargs) + tags = { + net.TARGET_HOST: kwargs["host"], + net.TARGET_PORT: kwargs["port"], + db.USER: kwargs["user"], + db.NAME: kwargs["database"], + } + + pin = Pin(app="mariadb", tags=tags) + + wrapped = TracedConnection(conn, pin=pin, cfg=config.mariadb) + pin.onto(wrapped) + return wrapped diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/molten/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/molten/__init__.py new file mode 100644 index 000000000..7a2b65665 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/molten/__init__.py @@ -0,0 +1,49 @@ +""" +The molten web framework is automatically traced by ``ddtrace`` when calling ``patch``:: + + from molten import App, Route + from ddtrace import patch_all; patch_all(molten=True) + + def hello(name: str, age: int) -> str: + return f'Hello {age} year old named {name}!' + app = App(routes=[Route('/hello/{name}/{age}', hello)]) + + +You may also enable molten tracing automatically via ``ddtrace-run``:: + + ddtrace-run python app.py + + +Configuration +~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.molten['distributed_tracing'] + + Whether to parse distributed tracing headers from requests received by your Molten app. + + Default: ``True`` + +.. py:data:: ddtrace.config.molten['service_name'] + + The service name reported for your Molten app. + + Can also be configured via the ``DD_SERVICE`` or ``DD_MOLTEN_SERVICE_NAME`` environment variables. + + Default: ``'molten'`` + +:ref:`All HTTP tags ` are supported for this integration. + +""" +from ...utils.importlib import require_modules + + +required_modules = ["molten"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from . import patch as _patch + + patch = _patch.patch + unpatch = _patch.unpatch + + __all__ = ["patch", "unpatch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/molten/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/molten/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ca403779484c7416519934da7e807b906c31577f GIT binary patch literal 1415 zcma)6-EP}96sBy)PJ|ZeBb)*S91pRTZfk+4O|T#d&;d=hrQW)!C@9*bC8iRoQIu1s z=x+7^#a>|ddxX7;Z+F%EUF~XzlJhfc7b74_BoDvyo$s8ZZ|~mq3696V{=)w35b}px z+#Vaa_yV8u3@1(^!ad%~yu1~)Ji^8K@q!n$vB#+29HIf3vJ$jEP+*4 z=gi6}%d!Pn!={46WC3x^dA*8bxJdD)Gmt95gM!&<8pmPi`+#F+bbbXd^FkX72Su?B zXS%ZD#$p8rE0Y?8BIXmg{@iLv?u|_)1`RkDGe||2=`Dq%Lny7;2A1HLRCWNveYX?V zUK|%AY-JS8`p?`Qc!!qnVIi0SopEpw9NxLt`}_U+5-SSWb9}dqqg(F1s%dZagW7A* z7zZ^_f4iYFc#Y)zA9MvdTL8;S4MMR=b}h2g%}Mo7ZRe8Rs5&&2x{+|DjoXA`VNowjPtVZdt4fhh&tGObI&1(6EIg(;gf8}VZ$N?W4PQz7R~sVsD5;H0T9N4v&85;In1 zHpGx!I^+2Nku8Op%c-C)M=O@DdRNH7bMlE&~`a zQaU=KFOJW?`||J@%NtSZos+MgkB?8U4(RE@i8~c)E{#^XP!{IQNSEmHUorL@wfip4 z(1=+WA7mLk8;`#R%aRhFrq&ii{7g{O{TP_RpsM0lAU4Hr#G)2SlWKJF5u29`yyd2o zT^d1i%`5aCbsKLVz2P@`am-G((2Ts{nT zC+gyS=kfWc=HX|m*r9I|wYz)y`HmY&5sZUE7DK5@iLze4L8&&q5809^jIuGu&yDi`Acz0S|uiN??L%+nr literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/molten/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/molten/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7e972a68a8b34620865eb76fa782a524e87d7495 GIT binary patch literal 4145 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 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/molten/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/molten/patch.py new file mode 100644 index 000000000..3e13c7493 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/molten/patch.py @@ -0,0 +1,164 @@ +import molten + +from ddtrace.vendor import wrapt +from ddtrace.vendor.wrapt import wrap_function_wrapper as _w + +from .. import trace_utils +from ... import Pin +from ... import config +from ...constants import ANALYTICS_SAMPLE_RATE_KEY +from ...constants import SPAN_MEASURED_KEY +from ...ext import SpanTypes +from ...internal.compat import urlencode +from ...utils.formats import asbool +from ...utils.formats import get_env +from ...utils.importlib import func_name +from ...utils.version import parse_version +from ...utils.wrappers import unwrap as _u +from .wrappers import MOLTEN_ROUTE +from .wrappers import WrapperComponent +from .wrappers import WrapperMiddleware +from .wrappers import WrapperRenderer +from .wrappers import WrapperRouter + + +MOLTEN_VERSION = parse_version(molten.__version__) + +# Configure default configuration +config._add( + "molten", + dict( + _default_service="molten", + app="molten", + distributed_tracing=asbool(get_env("molten", "distributed_tracing", default=True)), + ), +) + + +def patch(): + """Patch the instrumented methods""" + if getattr(molten, "_datadog_patch", False): + return + setattr(molten, "_datadog_patch", True) + + pin = Pin(app=config.molten["app"]) + + # add pin to module since many classes use __slots__ + pin.onto(molten) + + _w(molten.BaseApp, "__init__", patch_app_init) + _w(molten.App, "__call__", patch_app_call) + + +def unpatch(): + """Remove instrumentation""" + if getattr(molten, "_datadog_patch", False): + setattr(molten, "_datadog_patch", False) + + # remove pin + pin = Pin.get_from(molten) + if pin: + pin.remove_from(molten) + + _u(molten.BaseApp, "__init__") + _u(molten.App, "__call__") + + +def patch_app_call(wrapped, instance, args, kwargs): + """Patch wsgi interface for app""" + pin = Pin.get_from(molten) + + if not pin or not pin.enabled(): + return wrapped(*args, **kwargs) + + # DEV: This is safe because this is the args for a WSGI handler + # https://www.python.org/dev/peps/pep-3333/ + environ, start_response = args + + request = molten.http.Request.from_environ(environ) + resource = func_name(wrapped) + + # request.headers is type Iterable[Tuple[str, str]] + trace_utils.activate_distributed_headers( + pin.tracer, int_config=config.molten, request_headers=dict(request.headers) + ) + + with pin.tracer.trace( + "molten.request", + service=trace_utils.int_service(pin, config.molten), + resource=resource, + span_type=SpanTypes.WEB, + ) as span: + span.set_tag(SPAN_MEASURED_KEY) + # set analytics sample rate with global config enabled + span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, config.molten.get_analytics_sample_rate(use_global_config=True)) + + @wrapt.function_wrapper + def _w_start_response(wrapped, instance, args, kwargs): + """Patch respond handling to set metadata""" + + pin = Pin.get_from(molten) + if not pin or not pin.enabled(): + return wrapped(*args, **kwargs) + + status, headers, exc_info = args + code, _, _ = status.partition(" ") + + try: + code = int(code) + except ValueError: + pass + + if not span.get_tag(MOLTEN_ROUTE): + # if route never resolve, update root resource + span.resource = u"{} {}".format(request.method, code) + + trace_utils.set_http_meta(span, config.molten, status_code=code) + + return wrapped(*args, **kwargs) + + # patching for extracting response code + start_response = _w_start_response(start_response) + + url = "%s://%s:%s%s" % ( + request.scheme, + request.host, + request.port, + request.path, + ) + query = urlencode(dict(request.params)) + trace_utils.set_http_meta( + span, config.molten, method=request.method, url=url, query=query, request_headers=request.headers + ) + + span.set_tag("molten.version", molten.__version__) + return wrapped(environ, start_response, **kwargs) + + +def patch_app_init(wrapped, instance, args, kwargs): + """Patch app initialization of middleware, components and renderers""" + # allow instance to be initialized before wrapping them + wrapped(*args, **kwargs) + + # add Pin to instance + pin = Pin.get_from(molten) + + if not pin or not pin.enabled(): + return + + # Wrappers here allow us to trace objects without altering class or instance + # attributes, which presents a problem when classes in molten use + # ``__slots__`` + + instance.router = WrapperRouter(instance.router) + + # wrap middleware functions/callables + instance.middleware = [WrapperMiddleware(mw) for mw in instance.middleware] + + # wrap components objects within injector + # NOTE: the app instance also contains a list of components but it does not + # appear to be used for anything passing along to the dependency injector + instance.injector.components = [WrapperComponent(c) for c in instance.injector.components] + + # but renderers objects + instance.renderers = [WrapperRenderer(r) for r in instance.renderers] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/molten/wrappers.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/molten/wrappers.py new file mode 100644 index 000000000..b2068858e --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/molten/wrappers.py @@ -0,0 +1,105 @@ +import molten + +from ddtrace import config +from ddtrace.vendor import wrapt + +from .. import trace_utils +from ... import Pin +from ...utils.importlib import func_name + + +MOLTEN_ROUTE = "molten.route" + + +def trace_wrapped(resource, wrapped, *args, **kwargs): + pin = Pin.get_from(molten) + if not pin or not pin.enabled(): + return wrapped(*args, **kwargs) + + with pin.tracer.trace(func_name(wrapped), service=trace_utils.int_service(pin, config.molten), resource=resource): + return wrapped(*args, **kwargs) + + +def trace_func(resource): + """Trace calls to function using provided resource name""" + + @wrapt.function_wrapper + def _trace_func(wrapped, instance, args, kwargs): + pin = Pin.get_from(molten) + + if not pin or not pin.enabled(): + return wrapped(*args, **kwargs) + + with pin.tracer.trace( + func_name(wrapped), service=trace_utils.int_service(pin, config.molten, pin), resource=resource + ): + return wrapped(*args, **kwargs) + + return _trace_func + + +class WrapperComponent(wrapt.ObjectProxy): + """Tracing of components""" + + def can_handle_parameter(self, *args, **kwargs): + func = self.__wrapped__.can_handle_parameter + cname = func_name(self.__wrapped__) + resource = "{}.{}".format(cname, func.__name__) + return trace_wrapped(resource, func, *args, **kwargs) + + # TODO[tahir]: the signature of a wrapped resolve method causes DIError to + # be thrown since parameter types cannot be determined + + +class WrapperRenderer(wrapt.ObjectProxy): + """Tracing of renderers""" + + def render(self, *args, **kwargs): + func = self.__wrapped__.render + cname = func_name(self.__wrapped__) + resource = "{}.{}".format(cname, func.__name__) + return trace_wrapped(resource, func, *args, **kwargs) + + +class WrapperMiddleware(wrapt.ObjectProxy): + """Tracing of callable functional-middleware""" + + def __call__(self, *args, **kwargs): + func = self.__wrapped__.__call__ + resource = func_name(self.__wrapped__) + return trace_wrapped(resource, func, *args, **kwargs) + + +class WrapperRouter(wrapt.ObjectProxy): + """Tracing of router on the way back from a matched route""" + + def match(self, *args, **kwargs): + # catch matched route and wrap tracer around its handler and set root span resource + func = self.__wrapped__.match + route_and_params = func(*args, **kwargs) + + pin = Pin.get_from(molten) + if not pin or not pin.enabled(): + return route_and_params + + if route_and_params is not None: + route, params = route_and_params + + route.handler = trace_func(func_name(route.handler))(route.handler) + + # update root span resource while we know the matched route + resource = "{} {}".format( + route.method, + route.template, + ) + root_span = pin.tracer.current_root_span() + root_span.resource = resource + + # if no root route set make sure we record it based on this resolved + # route + if root_span and not root_span.get_tag(MOLTEN_ROUTE): + root_span.set_tag(MOLTEN_ROUTE, route.name) + + return route, params + + return route_and_params diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mongoengine/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mongoengine/__init__.py new file mode 100644 index 000000000..cd76b8c1a --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mongoengine/__init__.py @@ -0,0 +1,30 @@ +"""Instrument mongoengine to report MongoDB queries. + +``patch_all`` will automatically patch your mongoengine connect method to make it work. +:: + + from ddtrace import Pin, patch + import mongoengine + + # If not patched yet, you can patch mongoengine specifically + patch(mongoengine=True) + + # At that point, mongoengine is instrumented with the default settings + mongoengine.connect('db', alias='default') + + # Use a pin to specify metadata related to this client + client = mongoengine.connect('db', alias='master') + Pin.override(client, service="mongo-master") +""" + +from ...utils.importlib import require_modules + + +required_modules = ["mongoengine"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + from .patch import trace_mongoengine + + __all__ = ["patch", "trace_mongoengine"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mongoengine/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mongoengine/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6a4ff442c42c05800966704ee4985c6b5717d78a GIT binary patch literal 1059 zcmZuv%Wl&^6dgNBo0>vF{J^Lz(x5bMc~nY85TFXF3lNl6Aa9(B=O&rfo^fZ!35mpt z4?z3?dw!`~R{aH5T-#|d5+iv$k9*HKbM8I6ecK~A4t{)Riy9%n@=bMY;N}H><^(5B z0>T~c#!lh}u0wcj>;yG%q4w4J_AsbJ-5l{cZ^VtH5j60=&ew3g7OdfVoj1pBu>O%e zCGW`z`9%1}2h#S~YnPB?X{^o?kd`J&juptUkU*`X8d9Y#eUqOay`q;HXaT0{d4oa9 zY%~d391jL`CgPZ~%<05#iaK7T0vHGW`!`q@;uhdB9E-&nXgi0aREzQ~ILo9Ugif z#WB(;kxuzEcV5pXv3Z6=4 zcj?Pciw|2J%3{GxzqQP2t*FloP)1WBbK^?g^V~|tnPph4m|0}SJ3B!sQ7o{e1y#AE z{i@pkb0*AK&`6nA0UhtEDQGP??35TCe1o2f2>RQF{@pTRyX{?L1CzFMwWZ-Q6B@!q z@hk=t+^RhE+9`t?g~=FwuP8CbuKG^bF+?|TdfRSZt%o63Q5XhWnH90=mQ|0%P-Ekg z;O>%)uh_N{VGMeFbMO}~$HLwW!=FSqbB=ua{l!nUn4)q1lxcsYVh-B)i+#U0l(X}_ zgY!pv|FKN>!p~#b>n)z_8nu5GM6w$-imPK7fY|)ey^Wg8&AKqCj?cNP_}e3`NH(awSS2X{XletR4CS z-SYx{7_FWB3Y~hSYeYd0;N9^^-hKBSZ-&DjLAm_-BmPAR`K>o?ad7himt3F%0yHNP z0SoM$7FJ|Y0uH#jT{w|L2_q{v>Ocp)2NHSULl>>jEa*WW2H!0h!svnQXw(IIO@fm> z<|K=~jXI!B4_GBw5-SFKgrY;E&myiW23IBLEK!NR-xe1)SGeRWREj7HR9VX2(VdOR z$TfMi7LAu2UI$L&Nv2|@MAI)yo|cTK8D~OgY5e8CYmY?qF<{i6I;=#tK}g{y7U7pN z2PR}#&%*gFzh6x+S8v4ZEw84_Zz-S8>x-$Dv2R+kgbh=hqE+@x7g*b$b$Bk<;n3)R?0|!DhvqE=ka;G~K(N$!yQKyLTlm z<6I>0DTd?+80DzHG}oN+7jjBf?=z@qy##M6evn@f7$TpIoaF0_&&9d1CVNpme-Rw$ZI8=YF1wp6o;yIOb~L!pf@ z<80*$}966;ri4XX(DSkT|R%cLXd7vzXsc`wFZ9~-HcWg+_r%Sb)m z>O-?=rPd+j>l4mPRa(ybSO^!<`>#sXM{eL#79TzR;ivBppXvmogGSi~k_JZB(>@-C zx}yCS=S7_x!}%5YXO*?OT;^70%UoyErWR70Y;`v~I94as{=@2?zI$J_`}}mK4h~ii z_EBbS_Bw0J_D-pc1?cW9SM9;s*<5BrU;ph;)#~y%+n^+Koln{`ji2ldSO#Gu^afph z@hP8^>wyc35rZHWc;h83^B7b|D$>!;`#|6MmR8=SkHEeQxJB z^vlV5FWgIDep@R2ImSA53nPKW0x;toPX;RxI)YyiB=k$~e;`;#k7K;4^0@^5w>h6T zVo{?#;{5F*uZMqP&P7u|{|K)B3RJ&4@Lfo(y3x)}48bnk=Y<0sL?VG!MuGEw(wFz$dWh7@e%=bilBk>mYe*HNFD93ySSsyO&alMr)*w?Yum$-~qqV&FPt|w9(cE SRFBqEe&^m4VxBmtM&ZBry+|Vf literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mongoengine/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mongoengine/patch.py new file mode 100644 index 000000000..4d0b25acf --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mongoengine/patch.py @@ -0,0 +1,21 @@ +import mongoengine + +from ...utils.deprecation import deprecated +from .trace import WrappedConnect + + +# Original connect function +_connect = mongoengine.connect + + +def patch(): + setattr(mongoengine, "connect", WrappedConnect(_connect)) + + +def unpatch(): + setattr(mongoengine, "connect", _connect) + + +@deprecated(message="Use patching instead (see the docs).", version="1.0.0") +def trace_mongoengine(*args, **kwargs): + return _connect diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mongoengine/trace.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mongoengine/trace.py new file mode 100644 index 000000000..3deeddef0 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mongoengine/trace.py @@ -0,0 +1,30 @@ +# 3p +# project +import ddtrace +from ddtrace.contrib.pymongo.client import TracedMongoClient +from ddtrace.ext import mongo as mongox +from ddtrace.vendor import wrapt + + +# TODO(Benjamin): we should instrument register_connection instead, because more generic +# We should also extract the "alias" attribute and set it as a meta +class WrappedConnect(wrapt.ObjectProxy): + """WrappedConnect wraps mongoengines 'connect' function to ensure + that all returned connections are wrapped for tracing. + """ + + def __init__(self, connect): + super(WrappedConnect, self).__init__(connect) + ddtrace.Pin(service=mongox.SERVICE, tracer=ddtrace.tracer).onto(self) + + def __call__(self, *args, **kwargs): + client = self.__wrapped__(*args, **kwargs) + pin = ddtrace.Pin.get_from(self) + if pin: + # mongoengine uses pymongo internally, so we can just piggyback on the + # existing pymongo integration and make sure that the connections it + # uses internally are traced. + client = TracedMongoClient(client) + ddtrace.Pin(service=pin.service, tracer=pin.tracer).onto(client) + + return client diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysql/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysql/__init__.py new file mode 100644 index 000000000..cefff53c6 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysql/__init__.py @@ -0,0 +1,76 @@ +""" +The mysql integration instruments the mysql library to trace MySQL queries. + + +Enabling +~~~~~~~~ + +The mysql integration is enabled automatically when using +:ref:`ddtrace-run` or :ref:`patch_all()`. + +Or use :ref:`patch()` to manually enable the integration:: + + from ddtrace import patch + patch(mysql=True) + + +Global Configuration +~~~~~~~~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.mysql["service"] + + The service name reported by default for mysql spans. + + This option can also be set with the ``DD_MYSQL_SERVICE`` environment + variable. + + Default: ``"mysql"`` + +.. py:data:: ddtrace.config.mysql["trace_fetch_methods"] + + Whether or not to trace fetch methods. + + Can also configured via the ``DD_MYSQL_TRACE_FETCH_METHODS`` environment variable. + + Default: ``False`` + + +Instance Configuration +~~~~~~~~~~~~~~~~~~~~~~ + +To configure the mysql integration on an per-connection basis use the +``Pin`` API:: + + from ddtrace import Pin + # Make sure to import mysql.connector and not the 'connect' function, + # otherwise you won't have access to the patched version + import mysql.connector + + # This will report a span with the default settings + conn = mysql.connector.connect(user="alice", password="b0b", host="localhost", port=3306, database="test") + + # Use a pin to override the service name for this connection. + Pin.override(conn, service='mysql-users') + + cursor = conn.cursor() + cursor.execute("SELECT 6*7 AS the_answer;") + + +Only the default full-Python integration works. The binary C connector, +provided by _mysql_connector, is not supported. + +Help on mysql.connector can be found on: +https://dev.mysql.com/doc/connector-python/en/ +""" +from ...utils.importlib import require_modules + + +# check `mysql-connector` availability +required_modules = ["mysql.connector"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + from .tracers import get_traced_mysql_connection + + __all__ = ["get_traced_mysql_connection", "patch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysql/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysql/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fedd2fdc17b2f2e578b2a26903cf423421826148 GIT binary patch literal 2198 zcma)7-E!MR6qX%3Kbn+q4;MSmMeI&2r3Avn#F=Ismt>|PY2s2a!$@oG+TK>$Rd!c) zoeOSw0EQRf8eS>4T=f;W;_OOxk`4tlV|gW=JwM;~owF|=J#rnm_I~>nf4b^8f0~E; z*BU%Lg`c{F8*%y$DUotqO3M9m$stRlQh$k->C(@oUmo`>w4#nlg;e8eQtemadxb2= z%gIWA1=cHMbyV)JzH+{BUO1P|cMe&*avCoFec5r`-k73frmkbexTYhCH4_}xO3N&v zTq~p(7BL%0EN4gyq$Q3hI-6a+c#f_!Dj8Lt>$+Wz2QlL#_ow1=KNLVAYHTS%IMX75 zun~^q8JdnMN0~A{T9OW1At5$aQ)c|2Si|x#L_(t6BE>oy2e7-|I9RL?L&)b`f-8F8 zY;FSkp^60a%tp;~v8CRFq1AF-1lLfC1QoFnOHv^gbJ`LL#*cN0=iVD}e^an?_Tn$F}}3JU$N2z6WB1 ztM28ur=4yX0#_3z1vi2)jwV<#LtgIoIFH#vVOX;axTJLu%+ts2&TViW*;y z;gL$iF&FwyaMl(Twt4i^&Pj0A?VX$-Up=hr!$my< zoTw?uJ%zTw9EAVh#2RXQ0xgod*GBgghM?CV|CGumSaKR!ZU$HZQAW7{f*Xbxj6<$R z7pMOwNHDT;+(c*i4R8uT7~BfET3(E-ObkF3bCM$qV%jQRZ=qqvZ3f$gCt*mKGDu`5 zGBg!@OQSKKP=uq1Di{qg2*82u00TRf3Z{qM{LgXoe%j2HIAw8Mh!j+7D2t?V6_W4O-ar{U&+|rDK#bZb}tUpJc?c@~{I|=#BD#4~hnio8jJ>7a`=fn@TZ(itT6# zY{EfNB78+qu<1^frTGAYhCHEhYDE3M-psgxNi!4~2v6{qJJvc?E#D_}l8-r{G4Y9r z{6&CfYV-9e_uY3(a0)g`w;PgPXH3!{5hRPLy4iRy(z{Y_Shb>r9iNP-4(y~M4=Vtr z$|@Mk3f#VFtlh2z0mKi2{zj%*th`*p=FlsHMgQX>G`PuQ_cs!z%t~GWk#NfAx4))-D%k{U>cb@Uhax5*0{`ZTzdzv9*E@T!Ka)>(dAbw49r6AB zo6mR5HA?-SpR%-R4rlP$oZO`Qvsps@;%Hj@1A!KIX6VC-B-!^B0OD=nUmoTP`^KX1Ghvd^6-et<@?7+RAUgM1PhD z`2&^9tqRIR==ygMl1Q48fWpX9Hs%4RXybrIARHEHX<2#Jvh>oKaV4lY z>;tX_RflKN*>NqXIa~o;59*Y};?*F&cg#*0yipy^%Ld3R zaz3t%7UUI>SLH$?f<<{%F1{qel3bG4V7w-;%NsCWmpA1*Fy4@Ki!|ST1M!eflUlx! zWH6Nbc{WIffEryD^<()c&$760k{oACdnzi-8xG%q#$*p9&td~ZInup6PXT$u*o1Mm zYfIyO{XDe`1{VqkvFUGzAFEl|BB-Jkt8hEl z=3FUqWvtkNslbor5{&bqjdFxN~K_Q)s0-LM6MAiQBu8Y7g>M#!9yj?zUHZ+1_Y*W%Q}KdoL@N!xzJB zb@kx0Wo*Nu-E9|1aVN>NiPH4W?m@A-zdw%KQaa+#N7&w|q}O)wwTlSS(JJ?|@I|EXg1{+S1e^7CG8fkSplBZi zp$%H65ZDdCeSwzKhWnfchO17hE7V!~6ijCgXU-hb4?+2n3|#HL^s$Oh$XDcu9#X?3 zpHL}|*&#FhCve!+bm^EM@(FuYp70Y&$%KRJ<<7r0z(_I8$g@Ug5{UPQ+uyT83QG}O zN|GZk$uq9Rgib9->{s?&AP*Kh9&jw{Cbz7$Y^kv{dQH`Pu-c^*RPM{9Z~FPTcyJFl z!z`5HX%I#ZZe4KMx}WC#DAf;I7azDh^kHlIB1hTp7jbd-4{;up?IF~BaT|@5d`1i-T+)3jHSeW{A5I)=p?l~K|pv*mu Ux8kjs5rol8{cqv>9z3Og0FB_eC;$Ke literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysql/__pycache__/tracers.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysql/__pycache__/tracers.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..044274c97bf3ca772abb74abd1826b7143b6663e GIT binary patch literal 546 zcmYjPyH4dW5Ve#0285_-Qc!Fph;w0ubR~q4Xctz9rAc#TGA87bhu1cNq=j}1et@1| zN=rqzHPw!@yhfTc9(iVbj_tFRl?g%HeSF}5F+SVmtMxEBK-ZfHK!9c>qTqm=Ik_9T zln57k(1*bv2Zk`ZCwDaJ0X-%D_ys%2@r#S-({=(vDiL!dpxHRn!js&@muZoBX`zka z;C<6Vc;-@gP{!I1n*KBhgV1-{VJ@`hiD-wFP&zG(*iL>o2Nx9mC%PUX7_y)X2N}>~ z-);MOt$%0HI4+Aq#HLiy>QQ}q^5gJ-j??gj9#@HO2Uj;XJ`pv>F|QSqDy@((tGHqp zWd?XUYo=^=Ufe9g-Nm+=?i4bd-zLRu)_f1GC6fJOGL>6a70y;6tCXHPPC&Ni!Y1 jUplrb`w(4IC+`c`AJC5W@)E8yv2X8E+ZiKg*`3gTWQ3Cp literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysql/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysql/patch.py new file mode 100644 index 000000000..c26a3771e --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysql/patch.py @@ -0,0 +1,57 @@ +import mysql.connector + +from ddtrace import Pin +from ddtrace import config +from ddtrace.contrib.dbapi import TracedConnection +from ddtrace.vendor import wrapt + +from ...ext import db +from ...ext import net +from ...utils.formats import asbool +from ...utils.formats import get_env + + +config._add( + "mysql", + dict( + _default_service="mysql", + trace_fetch_methods=asbool(get_env("mysql", "trace_fetch_methods", default=False)), + ), +) + +CONN_ATTR_BY_TAG = { + net.TARGET_HOST: "server_host", + net.TARGET_PORT: "server_port", + db.USER: "user", + db.NAME: "database", +} + + +def patch(): + wrapt.wrap_function_wrapper("mysql.connector", "connect", _connect) + # `Connect` is an alias for `connect`, patch it too + if hasattr(mysql.connector, "Connect"): + mysql.connector.Connect = mysql.connector.connect + + +def unpatch(): + if isinstance(mysql.connector.connect, wrapt.ObjectProxy): + mysql.connector.connect = mysql.connector.connect.__wrapped__ + if hasattr(mysql.connector, "Connect"): + mysql.connector.Connect = mysql.connector.connect + + +def _connect(func, instance, args, kwargs): + conn = func(*args, **kwargs) + return patch_conn(conn) + + +def patch_conn(conn): + + tags = {t: getattr(conn, a) for t, a in CONN_ATTR_BY_TAG.items() if getattr(conn, a, "") != ""} + pin = Pin(app="mysql", tags=tags) + + # grab the metadata from the conn + wrapped = TracedConnection(conn, pin=pin, cfg=config.mysql) + pin.onto(wrapped) + return wrapped diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysql/tracers.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysql/tracers.py new file mode 100644 index 000000000..439c0d867 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysql/tracers.py @@ -0,0 +1,8 @@ +import mysql.connector + +from ...utils.deprecation import deprecated + + +@deprecated(message="Use patching instead (see the docs).", version="1.0.0") +def get_traced_mysql_connection(*args, **kwargs): + return mysql.connector.MySQLConnection diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysqldb/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysqldb/__init__.py new file mode 100644 index 000000000..3b555c13c --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysqldb/__init__.py @@ -0,0 +1,74 @@ +"""The mysqldb integration instruments the mysqlclient library to trace MySQL queries. + + +Enabling +~~~~~~~~ + +The integration is enabled automatically when using +:ref:`ddtrace-run` or :ref:`patch_all()`. + +Or use :ref:`patch()` to manually enable the integration:: + + from ddtrace import patch + patch(mysqldb=True) + + +Global Configuration +~~~~~~~~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.mysqldb["service"] + + The service name reported by default for spans. + + This option can also be set with the ``DD_MYSQLDB_SERVICE`` environment + variable. + + Default: ``"mysql"`` + +.. py:data:: ddtrace.config.mysqldb["trace_fetch_methods"] + + Whether or not to trace fetch methods. + + Can also configured via the ``DD_MYSQLDB_TRACE_FETCH_METHODS`` environment variable. + + Default: ``False`` + + +Instance Configuration +~~~~~~~~~~~~~~~~~~~~~~ + +To configure the integration on an per-connection basis use the +``Pin`` API:: + + # Make sure to import MySQLdb and not the 'connect' function, + # otherwise you won't have access to the patched version + from ddtrace import Pin + import MySQLdb + + # This will report a span with the default settings + conn = MySQLdb.connect(user="alice", passwd="b0b", host="localhost", port=3306, db="test") + + # Use a pin to override the service. + Pin.override(conn, service='mysql-users') + + cursor = conn.cursor() + cursor.execute("SELECT 6*7 AS the_answer;") + + +This package works for mysqlclient. Only the default full-Python integration works. The binary C connector provided by +_mysql is not supported. + +Help on mysqlclient can be found on: +https://mysqlclient.readthedocs.io/ + +""" +from ...utils.importlib import require_modules + + +required_modules = ["MySQLdb"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + + __all__ = ["patch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysqldb/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysqldb/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c47b3ddf81132a5be6d9d5580ea93e660ac6b173 GIT binary patch literal 2078 zcma)7%W@+%6m2`Np(|7216bU07D=e2onatUOq`nFcu1-yB$Id;FhzB%Te7W5x73l; zcANzpK7ir}*z-%-vYKCD#g)392af_(xvj_5eVlvl)tjeJeGkXZufGtszw9xD+_oMim(oMuVBjBq_%YNO`P? znu3vF6iFDIOfO!)f~$fl&UE1Wepiq<G@Fn#ox)_q z1QgodZYefsMU*-#TdELy)f}h$5l984L{3aH3i0;(#@^k0KSEDV6{=zn#bzRSAN|jW zD4f;O7Z>USFIz3&2OI+>GpK9>&vK~@I79B#&DJXv+dWmV4aDH9RK_HQjuZnvEJ|4K z@R;FZ|9w9QAfL7j z)W84{(m4?}Cm2O<#5Ks=zqDwo=Dz-$6DW=4|gD#EuTTry>|ETp+7 zo0Ek~wzS`w1yf>HD1>3m$=~DYogZ|%;mdBXa}=I*dq<~-7ms86_rP8vTFi#!A7g0{ zfwcec)Y|0sB;2|6SAGFj8v33ywS_W*B`!BH(U>U9TLi$5qBAbg%Y(Dy868*Qgj{0Y zFi?w2MMzhLSjI%q5;3I6TBUXk28D3iHyveHL0WJp9F0t60TU_K42;N_0Z9_3vFNe5 zQGw&Og_9|beZgJ+r)oeS-PvPb%IaAynK$8STG14Uj`Z);_Vn$BdJZhkxFbOc52VDD%#JVZ|`gZjoS^wa2Lzd zR`n{p!SD&>T-b0W#;7=TsLyIEaDo_IFgsnhZfwrZ+iQ;RTh=GNR_RR&rIEF5n|BeE ztM!dCBzF|BYnBv-tv4>ZuezNc?0oz=99&qyL#);bQ(ssgv1cL=a*|w!a@}e+9|#4C5sFSyndpGartiO> z!*^_>cDth3RlyYtGf9h->HbnhZ<|x!@)YgM51XWUq{W-$t#M@iYHod2X@p*XC=>^*;ezrmTkm literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysqldb/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysqldb/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..335a59852fb4e5d511920b200babaf8b18948130 GIT binary patch literal 2066 zcmZ`)&2JM&6rb6d{fJ{Hh9p4vDy{k_{F1>Q z;7Nbd;EAN%tN0azOMs{RDT61I>0Z@8LrKJ5o)zV2MpU9oG$qPnN=%FD5j&vb%#rm9 zEQlt%v*K)2>duK7py$MF%>46WPMmv2{0ri|xB%oGlfhAXr)Hx+}LE1D@cO=E{)Vwu49Rwm&p~yNx9)ffC zn%aG^yYa*K;JZ5xtuCF!Si2B^jCaj3aSN4_au$r#ZV@Tuv=!`VFBl)w?p9c!_%)GN z;Fux*3>vwN2ce1@gHSfwSt24?H1=*ZmNwI!?!sdCro8cGnlA)TJL%HW-d77aI(ehn z$m9HaoE9oflIw%Ld}(*L7d3=1={a6Ox03N@W1Pf>@vNTjYTKY|U`J5}LaZ|7w9L!2 zL~+WGK=%K+Sclz{Tok8s0lC3kK>XPO6ujbqIK=#xkpUXZY>=%P86apQ%!0EfXW+~C z*c}ZW9Z^!}N zXG78j;}q-iWuk1shR_K|7MSjU8(g_vPuNFn-yZUp6GQtIrDSNEXn&v%4MBC7f#Noh z7^3j&tzWEt3ey5L)*;4*|`S)Hx(WHc$Svp74!PdLUXX ziP9R^R((NJrRlb&`D;sl20G;DNK6(@?n=udZmCw*%U95Z0xx`rmWW5n#3FlFPr_bb zPO?^*6f5nn*#UUaSxC5qiTO{EUJ&&xh^)tJT|uZv+wr? zQ7ST7NBv4-A|)2GX3Tb{!bJVf2Q&p?(iTK zLUT;aae%&kxY5-7@y6YC&DT~R+?6-+I9?j2xlAz|uTItdNtV6kUMBiUw1Ocn7J+ye g3w+=n8b6Fv%RJS${4Jm0#G>Q*FL%9Zuj1MN0ZmBQga7~l literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysqldb/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysqldb/patch.py new file mode 100644 index 000000000..e08f46547 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/mysqldb/patch.py @@ -0,0 +1,75 @@ +# 3p +import MySQLdb + +# project +from ddtrace import Pin +from ddtrace import config +from ddtrace.contrib.dbapi import TracedConnection +from ddtrace.vendor.wrapt import wrap_function_wrapper as _w + +from ...ext import db +from ...ext import net +from ...utils.formats import asbool +from ...utils.formats import get_env +from ...utils.wrappers import unwrap as _u + + +config._add( + "mysqldb", + dict( + _default_service="mysql", + trace_fetch_methods=asbool(get_env("mysqldb", "trace_fetch_methods", default=False)), + ), +) + +KWPOS_BY_TAG = { + net.TARGET_HOST: ("host", 0), + db.USER: ("user", 1), + db.NAME: ("db", 3), +} + + +def patch(): + # patch only once + if getattr(MySQLdb, "__datadog_patch", False): + return + setattr(MySQLdb, "__datadog_patch", True) + + # `Connection` and `connect` are aliases for + # `Connect`; patch them too + _w("MySQLdb", "Connect", _connect) + if hasattr(MySQLdb, "Connection"): + _w("MySQLdb", "Connection", _connect) + if hasattr(MySQLdb, "connect"): + _w("MySQLdb", "connect", _connect) + + +def unpatch(): + if not getattr(MySQLdb, "__datadog_patch", False): + return + setattr(MySQLdb, "__datadog_patch", False) + + # unpatch MySQLdb + _u(MySQLdb, "Connect") + if hasattr(MySQLdb, "Connection"): + _u(MySQLdb, "Connection") + if hasattr(MySQLdb, "connect"): + _u(MySQLdb, "connect") + + +def _connect(func, instance, args, kwargs): + conn = func(*args, **kwargs) + return patch_conn(conn, *args, **kwargs) + + +def patch_conn(conn, *args, **kwargs): + tags = { + t: kwargs[k] if k in kwargs else args[p] for t, (k, p) in KWPOS_BY_TAG.items() if k in kwargs or len(args) > p + } + tags[net.TARGET_PORT] = conn.port + pin = Pin(app="mysql", tags=tags) + + # grab the metadata from the conn + wrapped = TracedConnection(conn, pin=pin, cfg=config.mysqldb) + pin.onto(wrapped) + return wrapped diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/psycopg/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/psycopg/__init__.py new file mode 100644 index 000000000..0b7ed90d1 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/psycopg/__init__.py @@ -0,0 +1,65 @@ +""" +The psycopg integration instruments the psycopg2 library to trace Postgres queries. + + +Enabling +~~~~~~~~ + +The psycopg integration is enabled automatically when using +:ref:`ddtrace-run` or :ref:`patch_all()`. + +Or use :ref:`patch()` to manually enable the integration:: + + from ddtrace import patch + patch(psycopg=True) + + +Global Configuration +~~~~~~~~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.psycopg["service"] + + The service name reported by default for psycopg spans. + + This option can also be set with the ``DD_PSYCOPG_SERVICE`` environment + variable. + + Default: ``"postgres"`` + +.. py:data:: ddtrace.config.psycopg["trace_fetch_methods"] + + Whether or not to trace fetch methods. + + Can also configured via the ``DD_PSYCOPG_TRACE_FETCH_METHODS`` environment variable. + + Default: ``False`` + + +Instance Configuration +~~~~~~~~~~~~~~~~~~~~~~ + +To configure the psycopg integration on an per-connection basis use the +``Pin`` API:: + + from ddtrace import Pin + import psycopg2 + + db = psycopg2.connect(connection_factory=factory) + # Use a pin to override the service name. + Pin.override(db, service="postgres-users") + + cursor = db.cursor() + cursor.execute("select * from users where id = 1") +""" +from ...utils.importlib import require_modules + + +required_modules = ["psycopg2"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .connection import connection_factory + from .patch import patch + from .patch import patch_conn + + __all__ = ["connection_factory", "patch", "patch_conn"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/psycopg/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/psycopg/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..73dfb0ec2d007f5581d24c6d89a8df6bdd353fe8 GIT binary patch literal 1773 zcma)7-EP}96qbKtJ6ehqdji6{v4>esytP=2I9TDhZ7#ZaiMwtZ28j}Bn}tNGB<0k( z*v%eb*bD4_uawK}s;{uC9a6HBtmrlxrVizg=jS^=e)-^m>%e;a+pqXV!*TvJhr4SR z4qw4ng|HFFcSwa)lS*3ks}+aT78SontF-oW<(Du0I<2bx5zGW7d3zPmGj8?#<_65bI3=RPInLgQFk17wxlRi>sVw9#JHvli8T`( zWTj=EQmz%!x0nG+SR}DrBQ21YIHqVSlwL@x(3_k}MwREf?u6qgVSM5KR4(_!h7_U( zO$oxe7AcsIagwaj)sk|QE2Cv7>3kRxVh#3W&W}qO#FG#Si3&u9b-WDVcBgy1l}|$O z=v;y-dKb2VzLH7K=)3;lg+mPeETUfsqx%?}s$Icru;@PbahS^Weo~ z_Wbe0?5|qreKS99~31`A~f0*$G{YuG1a*>8O(94gqa21)5d7{)^A}>bzt;UsvK7{}56#z~i-Z7!{p-X2 zQN*uaA3T0NkcXf1>>zl%;73Q-UmTdV$ojK>#y-A(skVKBH(Ii& nU$WE>0>+sR0xw(Nw2liBPYiSFA#C+Wd$mT@t<)=f=2QI};4(jL literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/psycopg/__pycache__/connection.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/psycopg/__pycache__/connection.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3b24d201f70ead90bedc02c33846fff744499649 GIT binary patch literal 3418 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 factory = connection_factor(my_tracer, service='my_db_service') + >>> conn = pyscopg2.connect(..., connection_factory=factory) + """ + + return functools.partial( + TracedConnection, + datadog_tracer=tracer, + datadog_service=service, + ) + + +class TracedCursor(cursor): + """Wrapper around cursor creating one span per query""" + + def __init__(self, *args, **kwargs): + self._datadog_tracer = kwargs.pop("datadog_tracer", None) + self._datadog_service = kwargs.pop("datadog_service", None) + self._datadog_tags = kwargs.pop("datadog_tags", None) + super(TracedCursor, self).__init__(*args, **kwargs) + + def execute(self, query, vars=None): # noqa: A002 + """just wrap the cursor execution in a span""" + if not self._datadog_tracer: + return cursor.execute(self, query, vars) + + with self._datadog_tracer.trace("postgres.query", service=self._datadog_service, span_type=SpanTypes.SQL) as s: + s.set_tag(SPAN_MEASURED_KEY) + if not s.sampled: + return super(TracedCursor, self).execute(query, vars) + + s.resource = query + s.set_tags(self._datadog_tags) + try: + return super(TracedCursor, self).execute(query, vars) + finally: + s.set_metric("db.rowcount", self.rowcount) + + def callproc(self, procname, vars=None): # noqa: A002 + """just wrap the execution in a span""" + return cursor.callproc(self, procname, vars) + + +class TracedConnection(connection): + """Wrapper around psycopg2 for tracing""" + + def __init__(self, *args, **kwargs): + + self._datadog_tracer = kwargs.pop("datadog_tracer", None) + self._datadog_service = kwargs.pop("datadog_service", None) + + super(TracedConnection, self).__init__(*args, **kwargs) + + # add metadata (from the connection, string, etc) + dsn = sql.parse_pg_dsn(self.dsn) + self._datadog_tags = { + net.TARGET_HOST: dsn.get("host"), + net.TARGET_PORT: dsn.get("port"), + db.NAME: dsn.get("dbname"), + db.USER: dsn.get("user"), + "db.application": dsn.get("application_name"), + } + + self._datadog_cursor_class = functools.partial( + TracedCursor, + datadog_tracer=self._datadog_tracer, + datadog_service=self._datadog_service, + datadog_tags=self._datadog_tags, + ) + + def cursor(self, *args, **kwargs): + """register our custom cursor factory""" + kwargs.setdefault("cursor_factory", self._datadog_cursor_class) + return super(TracedConnection, self).cursor(*args, **kwargs) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/psycopg/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/psycopg/patch.py new file mode 100644 index 000000000..b144f039b --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/psycopg/patch.py @@ -0,0 +1,193 @@ +# 3p +import psycopg2 +from psycopg2.sql import Composable + +from ddtrace import Pin +from ddtrace import config +from ddtrace.contrib import dbapi +from ddtrace.ext import db +from ddtrace.ext import net +from ddtrace.ext import sql +from ddtrace.vendor import wrapt + +from ...utils.formats import asbool +from ...utils.formats import get_env +from ...utils.version import parse_version + + +config._add( + "psycopg", + dict( + _default_service="postgres", + trace_fetch_methods=asbool(get_env("psycopg", "trace_fetch_methods", default=False)), + ), +) + +# Original connect method +_connect = psycopg2.connect + +PSYCOPG2_VERSION = parse_version(psycopg2.__version__) + + +def patch(): + """Patch monkey patches psycopg's connection function + so that the connection's functions are traced. + """ + if getattr(psycopg2, "_datadog_patch", False): + return + setattr(psycopg2, "_datadog_patch", True) + + wrapt.wrap_function_wrapper(psycopg2, "connect", patched_connect) + _patch_extensions(_psycopg2_extensions) # do this early just in case + + +def unpatch(): + if getattr(psycopg2, "_datadog_patch", False): + setattr(psycopg2, "_datadog_patch", False) + psycopg2.connect = _connect + _unpatch_extensions(_psycopg2_extensions) + + +class Psycopg2TracedCursor(dbapi.TracedCursor): + """TracedCursor for psycopg2""" + + def _trace_method(self, method, name, resource, extra_tags, *args, **kwargs): + # treat psycopg2.sql.Composable resource objects as strings + if isinstance(resource, Composable): + resource = resource.as_string(self.__wrapped__) + + return super(Psycopg2TracedCursor, self)._trace_method(method, name, resource, extra_tags, *args, **kwargs) + + +class Psycopg2FetchTracedCursor(Psycopg2TracedCursor, dbapi.FetchTracedCursor): + """FetchTracedCursor for psycopg2""" + + +class Psycopg2TracedConnection(dbapi.TracedConnection): + """TracedConnection wraps a Connection with tracing code.""" + + def __init__(self, conn, pin=None, cursor_cls=None): + if not cursor_cls: + # Do not trace `fetch*` methods by default + cursor_cls = Psycopg2FetchTracedCursor if config.psycopg.trace_fetch_methods else Psycopg2TracedCursor + + super(Psycopg2TracedConnection, self).__init__(conn, pin, config.psycopg, cursor_cls=cursor_cls) + + +def patch_conn(conn, traced_conn_cls=Psycopg2TracedConnection): + """Wrap will patch the instance so that its queries are traced.""" + # ensure we've patched extensions (this is idempotent) in + # case we're only tracing some connections. + _patch_extensions(_psycopg2_extensions) + + c = traced_conn_cls(conn) + + # fetch tags from the dsn + dsn = sql.parse_pg_dsn(conn.dsn) + tags = { + net.TARGET_HOST: dsn.get("host"), + net.TARGET_PORT: dsn.get("port"), + db.NAME: dsn.get("dbname"), + db.USER: dsn.get("user"), + "db.application": dsn.get("application_name"), + } + + Pin(app="postgres", tags=tags).onto(c) + + return c + + +def _patch_extensions(_extensions): + # we must patch extensions all the time (it's pretty harmless) so split + # from global patching of connections. must be idempotent. + for _, module, func, wrapper in _extensions: + if not hasattr(module, func) or isinstance(getattr(module, func), wrapt.ObjectProxy): + continue + wrapt.wrap_function_wrapper(module, func, wrapper) + + +def _unpatch_extensions(_extensions): + # we must patch extensions all the time (it's pretty harmless) so split + # from global patching of connections. must be idempotent. + for original, module, func, _ in _extensions: + setattr(module, func, original) + + +# +# monkeypatch targets +# + + +def patched_connect(connect_func, _, args, kwargs): + conn = connect_func(*args, **kwargs) + return patch_conn(conn) + + +def _extensions_register_type(func, _, args, kwargs): + def _unroll_args(obj, scope=None): + return obj, scope + + obj, scope = _unroll_args(*args, **kwargs) + + # register_type performs a c-level check of the object + # type so we must be sure to pass in the actual db connection + if scope and isinstance(scope, wrapt.ObjectProxy): + scope = scope.__wrapped__ + + return func(obj, scope) if scope else func(obj) + + +def _extensions_quote_ident(func, _, args, kwargs): + def _unroll_args(obj, scope=None): + return obj, scope + + obj, scope = _unroll_args(*args, **kwargs) + + # register_type performs a c-level check of the object + # type so we must be sure to pass in the actual db connection + if scope and isinstance(scope, wrapt.ObjectProxy): + scope = scope.__wrapped__ + + return func(obj, scope) if scope else func(obj) + + +def _extensions_adapt(func, _, args, kwargs): + adapt = func(*args, **kwargs) + if hasattr(adapt, "prepare"): + return AdapterWrapper(adapt) + return adapt + + +class AdapterWrapper(wrapt.ObjectProxy): + def prepare(self, *args, **kwargs): + func = self.__wrapped__.prepare + if not args: + return func(*args, **kwargs) + conn = args[0] + + # prepare performs a c-level check of the object type so + # we must be sure to pass in the actual db connection + if isinstance(conn, wrapt.ObjectProxy): + conn = conn.__wrapped__ + + return func(conn, *args[1:], **kwargs) + + +# extension hooks +_psycopg2_extensions = [ + (psycopg2.extensions.register_type, psycopg2.extensions, "register_type", _extensions_register_type), + (psycopg2._psycopg.register_type, psycopg2._psycopg, "register_type", _extensions_register_type), + (psycopg2.extensions.adapt, psycopg2.extensions, "adapt", _extensions_adapt), +] + +# `_json` attribute is only available for psycopg >= 2.5 +if getattr(psycopg2, "_json", None): + _psycopg2_extensions += [ + (psycopg2._json.register_type, psycopg2._json, "register_type", _extensions_register_type), + ] + +# `quote_ident` attribute is only available for psycopg >= 2.7 +if getattr(psycopg2, "extensions", None) and getattr(psycopg2.extensions, "quote_ident", None): + _psycopg2_extensions += [ + (psycopg2.extensions.quote_ident, psycopg2.extensions, "quote_ident", _extensions_quote_ident), + ] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylibmc/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylibmc/__init__.py new file mode 100644 index 000000000..1fc0595e7 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylibmc/__init__.py @@ -0,0 +1,32 @@ +"""Instrument pylibmc to report Memcached queries. + +``patch_all`` will automatically patch your pylibmc client to make it work. +:: + + # Be sure to import pylibmc and not pylibmc.Client directly, + # otherwise you won't have access to the patched version + from ddtrace import Pin, patch + import pylibmc + + # If not patched yet, you can patch pylibmc specifically + patch(pylibmc=True) + + # One client instrumented with default configuration + client = pylibmc.Client(["localhost:11211"] + client.set("key1", "value1") + + # Use a pin to specify metadata related to this client + Pin.override(client, service="memcached-sessions") +""" + +from ...utils.importlib import require_modules + + +required_modules = ["pylibmc"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .client import TracedClient + from .patch import patch + + __all__ = ["TracedClient", "patch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylibmc/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylibmc/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8e121b5bd46366239189bf8ae2406b0e8f108268 GIT binary patch literal 1121 zcmY+D%We}f6ox&ywxg7?VQ+;62}(1a0+muxgeaRXxRq9*L1^NRPZGDDOKnfWbjykr z;sw~V<9V`W)mLD}nM(p#8qdYY|M~r$GiO^{Ho>v~{TusPC*)WD^6%Khm#28?6P!5l z2)B4mT4BwrS%lZ8mRE-w)IVEa?|Ths=;yq_o3a_UycVuEcuTg!w%5kzHs6@myp4C{ z5qU#S$a}&!-;%z~zSIagj%9k!WdDS z!}H8RX%R=o=81|!%DGW2KyPLC*CHB}!9~dbo>r!hC#AET2WnIkH zVUtK^&{%<1=*n~7xf z;`(9CQwiE@SCnq7GN5ys7m6PheSjO91xwYyRn^4lWq)(o_I(})zVB_PMo2v@>xb>F zu;qz&yUM}WNw!0QPNM1R!?IPn{wtenVt@Po52Es4vzUHoPNKvdF*tLkoJlM>HkNbk zI-~Pwak0C9v8UVzQL^iQnnt71^~2qKOcUqSNkp=f*B3Q+=GVz6%R+FfE?nQS%tncG nr$XTPzKDeJ{b7eT)Kzbsrr literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylibmc/__pycache__/addrs.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylibmc/__pycache__/addrs.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..72e4835090ae79e46f272a9f36fc10d410d348df GIT binary patch literal 451 zcmYjOy-ve05I!dX#<-y7yaL=Rg^5-lI7Sb;?%L33+3G0+D%%kikhTNWJi*d80{2=uuBOv2(pWn zEODStr}8vUNvAJ;%t+HGzxJ(vpg#hyed=Gxbf*2zf~HB0tQm!zy#Tw|bN1Z!J9~d( zqN3q<^V`4r|2(g0|E7o0pM{4zD9JxiF^%bw*3n&Er@j#xuAy4fHC1c57Fsj1It90& z=2(&4DY`|~FQ8v?OR8^2hM!)XXRlgEVcBb4Z)vrcp zI@9j7u5q({);-6k`1yU^o%v8>H8$~BV-tLO-*ji0v8pxd{{i1xL(l3#Hb-?!@6)A))%z<; z9}Hej_I#0W&u2{VB;g6>l~;RyytdoRA3D4f_`xP;7%^im(Ns3L^rG$t$m6MccfHL6 zc~5kAb^~H^^haW+U-B^z%6JIQ9*1+-Iaj$K51hP}Wp zp`B)z*^6k;vL7%9?K$=myMp#Sdzt+Z?F^q~SJ^9%HK^eNyT-2L-5hJMSJ7VlO0zWg z1*qb+thOfnfV0IY^W^VyEv^ra)i4o{p=UP~N^ z^ifwlbhMA*q`4eC6&s1Jh*CnCL`nXPN@}St^?f6`B=uBp>8bUtkruwu_w=|T zjRTYEhp=mD$wI2_8wYlpKNs7j)JP%tuZ=xBwGYavM)UepvXbiU>b{<8UhNAlzG7)| z;$cPnQP=1%jdq>X!wO#;y7oYejW0E3Lkz|K`-_76l4Flj$-@bhK=Lg<5`Hg1mpdEb zHjka59SvqR`{}sc;zatf43!K>ij4OJ53oLS{Fph9!YFdqxg&U|yUm?2c3N9Ooxls@R(H8EnN`Asmi1$NT=a6qW+pr$D?ai?jE}QIvIT{T z8uXQwJbW2S&&!I8DO#C%FN`w_p4=5SO(_qFWtC^nn3b1y0^TF5$O??FZ*4S+nU(OU z1)o>478Ced)M*LJ7aK`tKLiJ8&0ZLT?Xu?uk)I@<_eA^d(R@#Y+mPt|wlC&e-H1VS z^ZhsHZ>`6V+Ba^t-x6=W9rtc{I~(z>Tm5%#&~KwRzc$|sdvB;6N6{PG{obwJ-4350 z>}A+eU=rc_{J`Mna}JxmT`>dxE~6wDP-%sVIc-?F@jt6DWz_YGY3NgWUGJYCwTI?Y znFTt1Gx(9LOq9DQ$&XQ`T3c(w3sM92Z%WwrzP@Kkvuz!~`VaNJLRt{-q=f_hP=hB} zWbvuBsy)${Ay z;49`%D7`XBn__~vLKChIt5>MkK=mV(gp5MN&P#etw`k|nV>?$Xj%}N^Jlysa9%$Rl zNOd?qHV$VrmopQbHgxeChN1Y1rx@~r4truAVfrXz^vNBR#6YF#{i!i_u22KQK z=CPi5ovJ2PM+8Enizvs_#R{jVYcn2k$wB%C5qvIb8vVrx>9b>7&KEg7bqlh>2`b(^ zkv5HfJVN`-7;ReO^pp#-yA^DL^jjyArqNGENRJzyTI2Mz&A_KkzkMQQ8dY9`OMpsat8ritCX^HPanQ=w=-4iL(=>6wXrX@~K*`{a}1B;29C(@?T zFGpyP+jqXm>8TfSA`i$@AbY-h(de&7$e$ZC@xdymr(cG1c%7|Ch7bUSe+0UzL7-Ew zDcl_!YH0FrM;MwJV`#YS_h1QOYRJ-0KtbH1>bP)6qyBSw7_M`A9%^~K8nW@;iEPj) z8)0K^OcbL_1PMtEvUGG;x|){TwDKyvBEUutgBa>9*91 z4wsu<0)%pN1YgE}2S{`P6f4f)r(mF()e1!-9blv8aPYgr)x^1)2tpXk4Ld8s4tyzv zxQ-WD!Ap<_5lWPv^fv@y@T~CJ{iT%$0~nB1b5=Z}{R|UicK#Yj4GSDS^xnWS8z_m& zSFM^}GfMhJqdz;QhG(bum=Y)#7%Jg+csN!Gcc26_&jWz;3*t^{qZAI15wvZB4XJ$! zRlF|=+&@PZ>I@r2O z>gw&uqe)##o@e*3t#G7OvEw^ry_Bg^F7zyq8z{TkpBQFzO+?*>sZx=wglmNEmI%08 zrtdw8dxZXUo>;+)HfS*#SQC@jXJhIa)E2Megf>9h^4{>Iuf_RKxhsh&qkXo@0Z z{zQpN{#2ADS#DPjs)s6a4y~|J6E9%h%ocFGZU^b$W*4cfTT;ge!k8#y*sZQDtbDk% z=6!T`b#3@?@9xT4Eb-HW|M7^x$dB5NC zqrphg^N_oHo{AD~adm0s^N$yoRC1|ut2~ZSQjvpyg~Amcew*GYn5Yn&f=&v+5G)Yt z)TUEWP;3?yMFjb7Zh|U8(T1{#K`tHeC;}JHeE(&dtWrD*JQq=t*HM*p%b2-XDwoXX z{%fVGfdsg8PNkiV3t44wyN4^IoCmTVu}sx0RirFjXXVFZ6O+}YNg|Xc$>iM2BZHKV zBmd6tpXi1mQMV1aKN~E7dqhDll?PsBGP6&|LR>Qn7(z3@=@S$Sabq_45?4B`E1Hx( yOHrjEJHH1J)3hET?%WMYBqOp?QX^SqrT20@|CFMw@;dT3hzg!}?xv8>&^_KSnzS0h+M_GbZ8bPWAx$-K@mkcRmPJ^ zap;unj7jaZjlNmCg;0@FP`k29lSLMTi9I_49#R7zC6kbpkrJ$ofZeTAE}&rBAqy7y z!)h>Eox|`VmxKA^G7p07?L`jqBQN5TNzYUCK%^Rzx{{fb*>;;u&?<{ zMk>DV%c{1UNK0C1

2G|8H5)Qurn~NZ=yW?!4XocfmF~YKoh@8G4Gz_s|Z;zS}lC naU*!65!hIO6Ak)ZpwInzsdtsF`6d` are supported for this integration. + +.. __: https://pylonsproject.org/about-pylons-framework.html +""" + +from ...utils.importlib import require_modules + + +required_modules = ["pylons.wsgiapp"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .middleware import PylonsTraceMiddleware + from .patch import patch + from .patch import unpatch + + __all__ = [ + "patch", + "unpatch", + "PylonsTraceMiddleware", + ] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b3f5b193366f11f5036bef8746d8fc1c4234fae3 GIT binary patch literal 1777 zcma)7-EJc_6rTLFX-$`M1J_o#*savgbYWR&w7a4-(NYOYX=mAG(8}Y)&cwwt9&At2 zREQfM0PzCc^GfBCt9=C|B#vh$X|`Gr9>wFakI&C{zH{>I&K=~zc=+3|^v8A2`_p}_ zj!pRZ6n-X#iFuL78muWBX)|gzJl0AYQHwWu>*vNVpG0lmHfOBO*5q2c9<9T9o2|=E z+KD>lx&!NtXam+8Y?Gm+6>UEEKJvcy&b${Md+XfW*`j~6Jr9Kwj!)-OWrh$eGRqT9 ztxy@ZnvS@^n*UI6V-2Pm!TQ==_;U1gOIz&6T#b%ZGWsBFR`8^g^Wk&^LH;WI znj2hFAT)oCF1JVk$p%=eY`u?5O20AUk|ve#e`5$ta2`O${zn?Rb;7$9^sZRz9h}kB z)uAJE1t_aR#ABsvt}TnV!XTUogH>Ku_h)c!EK;SeWL`V<2tELwO zl?^EW3qG>G(n&yvs<4Yn7;AuJru6sz#HJFxYC#*_X95Nm*J29IIDn)CVS5Z>KHD6JI)557XccGD__fQT0bFJ7#uxrA+Y4Qcm}$r?@G!WZ!F#RtmO zlds^(jXmf$%zS*zH^VTuF0I4SkGa0 ztZkgV6go-TlekA=>5|&gPD))lCgr4VZPGMKP|h8&{Eu^^9h57&J%i>(S>~p;=?16D U@FywGus@~hA4KqOFu;4{2W>4{D*ylh literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/__pycache__/constants.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/__pycache__/constants.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..fda3448776b5184c403895579f98b8618d0c1a08 GIT binary patch literal 254 zcmWIL<>g`k0<(D&6SaZ#V-N=!FakLaKwQiLBvKfn7*ZI688n%qMB?L95=#Qp*#IQvEa;qXeD({oFj=<9$6{Tzp)^9fMp~G8BPK0TaI}^$UtJ%Mwdc^~(~A z^waWlQc{bG^{Wi^EtB%fvkc6#jEW46^9l^&E7J2UEvrlnKp?$9KSaMEvp_d9uec;J zCr7ues=%_cGB;H}C8eY&F&RN8=jW9aWhUttROaO873%}JKn-~%#d-ymw>WHa^HWN5 MQtd#l`3%Gi0BaOS&Hw-a literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/__pycache__/middleware.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/__pycache__/middleware.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..81672a98334b0839bfdb8a8333e99a7b4ad0e832 GIT binary patch literal 3521 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 t?LbB$=hA5RD3=*`{8lKgx@ 0.9.7 + legacy_pylons = False +except ImportError: + # Pylons <= 0.9.7 + legacy_pylons = True diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/constants.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/constants.py new file mode 100644 index 000000000..082befab6 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/constants.py @@ -0,0 +1 @@ +CONFIG_MIDDLEWARE = "__datadog_middleware" diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/middleware.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/middleware.py new file mode 100644 index 000000000..1b2224b06 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/middleware.py @@ -0,0 +1,124 @@ +import sys + +from pylons import config +from webob import Request + +from ddtrace import config as ddconfig + +from .. import trace_utils +from ...constants import ANALYTICS_SAMPLE_RATE_KEY +from ...constants import SPAN_MEASURED_KEY +from ...ext import SpanTypes +from ...ext import http +from ...internal.compat import reraise +from ...internal.logger import get_logger +from ...utils.formats import asbool +from .constants import CONFIG_MIDDLEWARE +from .renderer import trace_rendering + + +log = get_logger(__name__) + + +class PylonsTraceMiddleware(object): + def __init__(self, app, tracer, service="pylons", distributed_tracing=None): + self.app = app + self._service = service + self._tracer = tracer + + if distributed_tracing is not None: + self._distributed_tracing = distributed_tracing + + # register middleware reference + config[CONFIG_MIDDLEWARE] = self + + # add template tracing + trace_rendering() + + @property + def _distributed_tracing(self): + return ddconfig.pylons.distributed_tracing + + @_distributed_tracing.setter + def _distributed_tracing(self, distributed_tracing): + ddconfig.pylons["distributed_tracing"] = asbool(distributed_tracing) + + def __call__(self, environ, start_response): + request = Request(environ) + trace_utils.activate_distributed_headers( + self._tracer, int_config=ddconfig.pylons, request_headers=request.headers + ) + + with self._tracer.trace("pylons.request", service=self._service, span_type=SpanTypes.WEB) as span: + span.set_tag(SPAN_MEASURED_KEY) + # Set the service in tracer.trace() as priority sampling requires it to be + # set as early as possible when different services share one single agent. + + # set analytics sample rate with global config enabled + span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, ddconfig.pylons.get_analytics_sample_rate(use_global_config=True)) + + trace_utils.set_http_meta(span, ddconfig.pylons, request_headers=request.headers) + + if not span.sampled: + return self.app(environ, start_response) + + # tentative on status code, otherwise will be caught by except below + def _start_response(status, *args, **kwargs): + """a patched response callback which will pluck some metadata.""" + if len(args): + response_headers = args[0] + else: + response_headers = kwargs.get("response_headers", {}) + http_code = int(status.split()[0]) + trace_utils.set_http_meta( + span, ddconfig.pylons, status_code=http_code, response_headers=response_headers + ) + return start_response(status, *args, **kwargs) + + try: + return self.app(environ, _start_response) + except Exception as e: + # store current exceptions info so we can re-raise it later + (typ, val, tb) = sys.exc_info() + + # e.code can either be a string or an int + code = getattr(e, "code", 500) + try: + int(code) + except (TypeError, ValueError): + code = 500 + trace_utils.set_http_meta(span, ddconfig.pylons, status_code=code) + + # re-raise the original exception with its original traceback + reraise(typ, val, tb=tb) + except SystemExit: + span.set_tag(http.STATUS_CODE, 500) + span.error = 1 + raise + finally: + controller = environ.get("pylons.routes_dict", {}).get("controller") + action = environ.get("pylons.routes_dict", {}).get("action") + + # There are cases where users re-route requests and manually + # set resources. If this is so, don't do anything, otherwise + # set the resource to the controller / action that handled it. + if span.resource == span.name: + span.resource = "%s.%s" % (controller, action) + + url = "%s://%s:%s%s" % ( + environ.get("wsgi.url_scheme"), + environ.get("SERVER_NAME"), + environ.get("SERVER_PORT"), + environ.get("PATH_INFO"), + ) + trace_utils.set_http_meta( + span, + ddconfig.pylons, + method=environ.get("REQUEST_METHOD"), + url=url, + query=environ.get("QUERY_STRING"), + ) + if controller: + span._set_str_tag("pylons.route.controller", controller) + if action: + span._set_str_tag("pylons.route.action", action) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/patch.py new file mode 100644 index 000000000..f79fbfa23 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/patch.py @@ -0,0 +1,52 @@ +import pylons.wsgiapp + +from ddtrace import Pin +from ddtrace import config +from ddtrace import tracer +from ddtrace.vendor import wrapt + +from ...utils.formats import asbool +from ...utils.formats import get_env +from ...utils.wrappers import unwrap as _u +from .middleware import PylonsTraceMiddleware + + +config._add( + "pylons", + dict( + distributed_tracing=asbool(get_env("pylons", "distributed_tracing", default=True)), + ), +) + + +def patch(): + """Instrument Pylons applications""" + if getattr(pylons.wsgiapp, "_datadog_patch", False): + return + + setattr(pylons.wsgiapp, "_datadog_patch", True) + wrapt.wrap_function_wrapper("pylons.wsgiapp", "PylonsApp.__init__", traced_init) + + +def unpatch(): + """Disable Pylons tracing""" + if not getattr(pylons.wsgiapp, "__datadog_patch", False): + return + setattr(pylons.wsgiapp, "__datadog_patch", False) + + _u(pylons.wsgiapp.PylonsApp, "__init__") + + +def traced_init(wrapped, instance, args, kwargs): + wrapped(*args, **kwargs) + + # set tracing options and create the TraceMiddleware + service = config._get_service(default="pylons") + Pin(service=service, tracer=tracer).onto(instance) + traced_app = PylonsTraceMiddleware( + instance, tracer, service=service, distributed_tracing=config.pylons.distributed_tracing + ) + + # re-order the middleware stack so that the first middleware is ours + traced_app.app = instance.app + instance.app = traced_app diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/renderer.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/renderer.py new file mode 100644 index 000000000..fad0360e3 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pylons/renderer.py @@ -0,0 +1,37 @@ +import pylons +from pylons import config + +from ddtrace.vendor.wrapt import wrap_function_wrapper as _w + +from ...utils import get_argument_value +from .compat import legacy_pylons +from .constants import CONFIG_MIDDLEWARE + + +def trace_rendering(): + """Patch all Pylons renderers. It supports multiple versions + of Pylons and multiple renderers. + """ + # patch only once + if getattr(pylons.templating, "__datadog_patch", False): + return + setattr(pylons.templating, "__datadog_patch", True) + + if legacy_pylons: + # Pylons <= 0.9.7 + _w("pylons.templating", "render", _traced_renderer) + else: + # Pylons > 0.9.7 + _w("pylons.templating", "render_mako", _traced_renderer) + _w("pylons.templating", "render_mako_def", _traced_renderer) + _w("pylons.templating", "render_genshi", _traced_renderer) + _w("pylons.templating", "render_jinja2", _traced_renderer) + + +def _traced_renderer(wrapped, instance, args, kwargs): + """Traced renderer""" + tracer = config[CONFIG_MIDDLEWARE]._tracer + with tracer.trace("pylons.render") as span: + template_name = get_argument_value(args, kwargs, 0, "template_name") + span.set_tag("template.name", template_name) + return wrapped(*args, **kwargs) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymemcache/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymemcache/__init__.py new file mode 100644 index 000000000..67676f7de --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymemcache/__init__.py @@ -0,0 +1,40 @@ +"""Instrument pymemcache to report memcached queries. + +``patch_all`` will automatically patch the pymemcache ``Client``:: + + from ddtrace import Pin, patch + + # If not patched yet, patch pymemcache specifically + patch(pymemcache=True) + + # Import reference to Client AFTER patching + import pymemcache + from pymemcache.client.base import Client + + # Use a pin to specify metadata related all clients + Pin.override(pymemcache, service='my-memcached-service') + + # This will report a span with the default settings + client = Client(('localhost', 11211)) + client.set("my-key", "my-val") + + # Use a pin to specify metadata related to this particular client + Pin.override(client, service='my-memcached-service') + +Pymemcache ``HashClient`` will also be indirectly patched as it uses ``Client`` +under the hood. +""" +from ...utils.importlib import require_modules + + +required_modules = ["pymemcache"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + from .patch import unpatch + + __all__ = [ + patch, + unpatch, + ] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymemcache/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymemcache/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8687c799c0f583c63b2aaf2bfbd16dfefe814167 GIT binary patch literal 1292 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 SGTpsDyghu87 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymemcache/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymemcache/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..010d1d6a8af3ea357f869b3321d53514219f7e7a GIT binary patch literal 1317 zcmaJ=OK;Oa5cc{V$4S%lLCdM)mIHAMs8S?^Kz)cF2vJ*JzF4l;B(@yKT5m&>6H>Wy zK;n$x$Y1i6Q~v@dX4XlQ1F)9AdF*Sxnb}vhnkB*5|MtZ{(Ix2@8p}fg<0<_4R{$ze zl1OeTiI8$44;5D>=u;AHsJpt*RnQIB5V{7s>6)PH)JQ5r%e9CUN^vEw#RceFWpyW(y)ovg%soNq&Wf%?vKNy4*45b&P zAZ+jO(CeLaz3#IYM_@PScJK7%+jqiyLtsBl)!r~a%R+jPL}AJcI}p+f5v}o#{gX7B$g?m^0mU6R63KmGV>_c3Yl54N>#2c+4`+*upZZNN36%DSK$_i%-~P8 z&+_*gCqD>g64>h9qV7?j`*hUzGM@*7 z(@tmlXctF6bIzSC%I-&L&iy30Kb~ft39xY}<;)KjGJp$Nbm1(hIwC{uY*J`K!zl6w zfJEv zJh`P!w@<_2XdLb=$m}57DDATfM04C__;6b_Rt3Rqgc?E}VFjQtyt04{3ByCc%FbP@ zxT=XDiiq9JG=$q?mD&dQ3ga>r*}Dfuj+-RNoC+D7XU`^V8@#Pe5r@0Eh`C)hPaE%c ztFwZ&!z<2Y*ud5R7Va!cMLV%YID!&3rl2G!vpZ-aC$@!vR0;zwz&>B%gcTKG{gspX gcsQb$N%#aGe|{IhB&KW=@yWJg5ldEqpskqoKVQ!;M*si- literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymemcache/client.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymemcache/client.py new file mode 100644 index 000000000..5e6ab24f3 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymemcache/client.py @@ -0,0 +1,220 @@ +import sys + +import pymemcache +from pymemcache.client.base import Client +from pymemcache.exceptions import MemcacheClientError +from pymemcache.exceptions import MemcacheIllegalInputError +from pymemcache.exceptions import MemcacheServerError +from pymemcache.exceptions import MemcacheUnknownCommandError +from pymemcache.exceptions import MemcacheUnknownError + +# 3p +from ddtrace import config +from ddtrace.vendor import wrapt + +# project +from ...constants import ANALYTICS_SAMPLE_RATE_KEY +from ...constants import SPAN_MEASURED_KEY +from ...ext import SpanTypes +from ...ext import memcached as memcachedx +from ...ext import net +from ...internal.compat import reraise +from ...internal.logger import get_logger +from ...pin import Pin + + +log = get_logger(__name__) + + +# keep a reference to the original unpatched clients +_Client = Client + + +class WrappedClient(wrapt.ObjectProxy): + """Wrapper providing patched methods of a pymemcache Client. + + Relevant connection information is obtained during initialization and + attached to each span. + + Keys are tagged in spans for methods that act upon a key. + """ + + def __init__(self, *args, **kwargs): + c = _Client(*args, **kwargs) + super(WrappedClient, self).__init__(c) + + # tags to apply to each span generated by this client + tags = _get_address_tags(*args, **kwargs) + + parent_pin = Pin.get_from(pymemcache) + + if parent_pin: + pin = parent_pin.clone(tags=tags) + else: + pin = Pin(tags=tags) + + # attach the pin onto this instance + pin.onto(self) + + def set(self, *args, **kwargs): + return self._traced_cmd("set", *args, **kwargs) + + def set_many(self, *args, **kwargs): + return self._traced_cmd("set_many", *args, **kwargs) + + def add(self, *args, **kwargs): + return self._traced_cmd("add", *args, **kwargs) + + def replace(self, *args, **kwargs): + return self._traced_cmd("replace", *args, **kwargs) + + def append(self, *args, **kwargs): + return self._traced_cmd("append", *args, **kwargs) + + def prepend(self, *args, **kwargs): + return self._traced_cmd("prepend", *args, **kwargs) + + def cas(self, *args, **kwargs): + return self._traced_cmd("cas", *args, **kwargs) + + def get(self, *args, **kwargs): + return self._traced_cmd("get", *args, **kwargs) + + def get_many(self, *args, **kwargs): + return self._traced_cmd("get_many", *args, **kwargs) + + def gets(self, *args, **kwargs): + return self._traced_cmd("gets", *args, **kwargs) + + def gets_many(self, *args, **kwargs): + return self._traced_cmd("gets_many", *args, **kwargs) + + def delete(self, *args, **kwargs): + return self._traced_cmd("delete", *args, **kwargs) + + def delete_many(self, *args, **kwargs): + return self._traced_cmd("delete_many", *args, **kwargs) + + def incr(self, *args, **kwargs): + return self._traced_cmd("incr", *args, **kwargs) + + def decr(self, *args, **kwargs): + return self._traced_cmd("decr", *args, **kwargs) + + def touch(self, *args, **kwargs): + return self._traced_cmd("touch", *args, **kwargs) + + def stats(self, *args, **kwargs): + return self._traced_cmd("stats", *args, **kwargs) + + def version(self, *args, **kwargs): + return self._traced_cmd("version", *args, **kwargs) + + def flush_all(self, *args, **kwargs): + return self._traced_cmd("flush_all", *args, **kwargs) + + def quit(self, *args, **kwargs): + return self._traced_cmd("quit", *args, **kwargs) + + def set_multi(self, *args, **kwargs): + """set_multi is an alias for set_many""" + return self._traced_cmd("set_many", *args, **kwargs) + + def get_multi(self, *args, **kwargs): + """set_multi is an alias for set_many""" + return self._traced_cmd("get_many", *args, **kwargs) + + def _traced_cmd(self, method_name, *args, **kwargs): + """Run and trace the given command. + + Any pymemcache exception is caught and span error information is + set. The exception is then reraised for the application to handle + appropriately. + + Relevant tags are set in the span. + """ + method = getattr(self.__wrapped__, method_name) + p = Pin.get_from(self) + + # if the pin does not exist or is not enabled, shortcut + if not p or not p.enabled(): + return method(*args, **kwargs) + + with p.tracer.trace( + memcachedx.CMD, + service=p.service, + resource=method_name, + span_type=SpanTypes.CACHE, + ) as span: + span.set_tag(SPAN_MEASURED_KEY) + # set analytics sample rate + span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, config.pymemcache.get_analytics_sample_rate()) + + # try to set relevant tags, catch any exceptions so we don't mess + # with the application + try: + span.set_tags(p.tags) + vals = _get_query_string(args) + query = "{}{}{}".format(method_name, " " if vals else "", vals) + span.set_tag(memcachedx.QUERY, query) + except Exception: + log.debug("Error setting relevant pymemcache tags") + + try: + return method(*args, **kwargs) + except ( + MemcacheClientError, + MemcacheServerError, + MemcacheUnknownCommandError, + MemcacheUnknownError, + MemcacheIllegalInputError, + ): + (typ, val, tb) = sys.exc_info() + span.set_exc_info(typ, val, tb) + reraise(typ, val, tb) + + +def _get_address_tags(*args, **kwargs): + """Attempt to get host and port from args passed to Client initializer.""" + tags = {} + try: + if len(args): + host, port = args[0] + tags[net.TARGET_HOST] = host + tags[net.TARGET_PORT] = port + except Exception: + log.debug("Error collecting client address tags") + + return tags + + +def _get_query_string(args): + """Return the query values given the arguments to a pymemcache command. + + If there are multiple query values, they are joined together + space-separated. + """ + keys = "" + + # shortcut if no args + if not args: + return keys + + # pull out the first arg which will contain any key + arg = args[0] + + # if we get a dict, convert to list of keys + if type(arg) is dict: + arg = list(arg) + + if type(arg) is str: + keys = arg + elif type(arg) is bytes: + keys = arg.decode() + elif type(arg) is list and len(arg): + if type(arg[0]) is str: + keys = " ".join(arg) + elif type(arg[0]) is bytes: + keys = b" ".join(arg).decode() + + return keys diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymemcache/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymemcache/patch.py new file mode 100644 index 000000000..3ecc839c1 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymemcache/patch.py @@ -0,0 +1,46 @@ +import pymemcache +import pymemcache.client.hash + +from ddtrace.ext import memcached as memcachedx +from ddtrace.pin import Pin +from ddtrace.pin import _DD_PIN_NAME +from ddtrace.pin import _DD_PIN_PROXY_NAME + +from .client import WrappedClient + + +_Client = pymemcache.client.base.Client +_hash_Client = pymemcache.client.hash.Client + +_HashClient_client_class = None +if hasattr(pymemcache.client.hash.HashClient, "client_class"): + _HashClient_client_class = pymemcache.client.hash.HashClient.client_class + + +def patch(): + if getattr(pymemcache.client, "_datadog_patch", False): + return + + setattr(pymemcache.client, "_datadog_patch", True) + setattr(pymemcache.client.base, "Client", WrappedClient) + setattr(pymemcache.client.hash, "Client", WrappedClient) + if _HashClient_client_class: + pymemcache.client.hash.HashClient.client_class = WrappedClient + + # Create a global pin with default configuration for our pymemcache clients + Pin(app=memcachedx.SERVICE, service=memcachedx.SERVICE).onto(pymemcache) + + +def unpatch(): + """Remove pymemcache tracing""" + if not getattr(pymemcache.client, "_datadog_patch", False): + return + setattr(pymemcache.client, "_datadog_patch", False) + setattr(pymemcache.client.base, "Client", _Client) + setattr(pymemcache.client.hash, "Client", _hash_Client) + if _HashClient_client_class: + pymemcache.client.hash.HashClient.client_class = _HashClient_client_class + + # Remove any pins that may exist on the pymemcache reference + setattr(pymemcache, _DD_PIN_NAME, None) + setattr(pymemcache, _DD_PIN_PROXY_NAME, None) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/__init__.py new file mode 100644 index 000000000..0ce08eb66 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/__init__.py @@ -0,0 +1,37 @@ +"""Instrument pymongo to report MongoDB queries. + +The pymongo integration works by wrapping pymongo's MongoClient to trace +network calls. Pymongo 3.0 and greater are the currently supported versions. +``patch_all`` will automatically patch your MongoClient instance to make it work. + +:: + + # Be sure to import pymongo and not pymongo.MongoClient directly, + # otherwise you won't have access to the patched version + from ddtrace import Pin, patch + import pymongo + + # If not patched yet, you can patch pymongo specifically + patch(pymongo=True) + + # At that point, pymongo is instrumented with the default settings + client = pymongo.MongoClient() + # Example of instrumented query + db = client["test-db"] + db.teams.find({"name": "Toronto Maple Leafs"}) + + # Use a pin to specify metadata related to this client + client = pymongo.MongoClient() + pin = Pin.override(client, service="mongo-master") +""" +from ...utils.importlib import require_modules + + +required_modules = ["pymongo"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + from .patch import trace_mongo_client + + __all__ = ["trace_mongo_client", "patch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..eb1f9cca3134a48727805acd827d9f68209421e4 GIT binary patch literal 1396 zcma)6O>Y}F5asH_b{0-u^w`rtIm9Y#t<$stY*a3kq|=zS;M zq4!;}x9Fw2Ke8{`_v|_QiHRFOuyM@a^%;AjtTT0q%0bnXS}im<4F)T196a^=#}DCk zjYguK#PRtOw{cQATo~@8RLSiwrx=!71HM*Y-f0mu|ntXK+cQ8 zPT*OW`|jifxDv21$Q>Hs2EkFzyf%jJ7Y*3D^4w6s3XP@QMC;6vp`qlj5Tpy0P@!jMaSXI>!9%2+o`wcl23a;jd7?^h zg_{Jqx+AThws3iFbO}NP-8K}S zNp;i)hmh;UHq4*QTY4R4gYGC~&$;Rp*;HaH%;mflD*y%_hu!h?+|)SUpgy23E;&`L z$pA#6>zuUU$bQT)F2LIVBe5dl?F<5TyV!}EDCa^Xq!a^x0(NMK^}Dq)VYZ!>l!KWumw0GZOlsM zI30xvJTAE<^N+^ydzw%-j;;<2zOKnpS*b-`piOr>+22QPz%Vq7B0mW>p*Wh{tc5p2 ziZS#T!@e8uUF~L>(0P`n2ep$ldaZQ|IWs+qNI&i}iEXNbQd&!m+x!ra0;b2J@yfD4 znc4F>{NtY{sf=8aPRWX!WUdQAbDCVE#zEel4ox zJgH=L97eJzj#rn}Y15RLbjII)=hQtTXGteOl4Vj!mt~Wxx!U`x)#q!kiv5TlgHQMS NJH0p>MElWx?_Vm~zCHi| literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/__pycache__/client.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/__pycache__/client.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..565a1509086e58ccf49218a3ceaffb448e131384 GIT binary patch literal 7434 zcmd5>TXWmS6~>+5Rn* zOk>o#-Y_f!w8r#$s$p8DpbgMzD=p|$J=4fqSwWkib5>r^Y0w30NYI&ju`z573p!gL zX^dKMx=`#>P<}7xfbA6KoRoNm2hEJHd{ysYkMPk{xBo9!b_IcAUL{ z=V>OdNaYuw689F%a%5DS-nzR1D!a5+bE?5j-h81{MYsGbwxk#+-z z<8DxAX2S{WdUIpL;ZGqo`dqqI;l5)xe6gOma&ODw+cZ3``YoqgrN#GOB!0U5X%vn` zB8Rxm&!u&lDNKEohR`8gY+qvrOW~=1E@_fwfEDRzc$HVG4!c2XovXW!7c5p4F<)HJ z>~s&|_Bo0`3gtlFRs87?U)nVJi9imOQ2kQgk-Z{lc^&nyla@dU)KJ=0I$9|2$+CF6 zrUwSzzZDpvT+?^)hOGywni-^P8M>MLh0@7}5{+jPR;etOl_=#q{JvXtEVa^Vm2*+X z^oDqhuza(3VPBi-L>Ih+@<*vPbP*K-5gj?#9}Y&F61?Whp%d9_(Cj&rAVvr4L>qAAJFp&#An;pdd2r``-$|; zgP9h0?^gn6=6;3GtT*e-;r>kfwV4ZR-hKU6EKD@+ZGcF^^)5s4qMFr&?=cl_aeHtpaLoZ7SmC%By|ra%}=ALtnnjw z@F^6LN%pnQn~ivVvRL0COT(~aY471R|9MN>A6mxlQ>0p6rQtxeGPd1lvaLGN*zUcp zNRh^+#%pXG$NU&hCS0%Pr5_Zg8l~cc%LToFMxn*|E zikGOrCwMXF12lD+Iyq={`Mpft zHN9hyv*@HkD1$78YIn(Il(p>#9R9*#5KSKT3m*@4HZKodgEMdxe>fu0aHw zGQWa8iQn{q6ub;evkbH;<(VwYa-d9yTy>rm9!ZGYStrMaSW(b

YLh+%TWP0{%yh zqbd3|E;14UmZ)P;-#$W9SKr3_MZ2$U3WjtqVPmMw;r9QAf=N4YD)4pibo^pFN8t=w zrregi6Pgq#n;Cvymgp}~I~o)I@Q&muA4!2;GoXRc!yPqL_^nW-yJPN2zC}h9sx{)5 z5@c&R(0QhQq3&p*)+q!-LTiaC)`ml+HnOKci{ttz>SIZL9Q1?;qLZN(D!UjJ9H~up zj_yf2dZ;59r}OUF-nZ zg%5)f<>Lrwt`A=Z0K&=RM(O#7Ri_oWO)pXrMx(UTt?&C?VK-6A@hWR|hebv}VUtzKVRjI?UAUXRRV zupeo7#g9fQvcyZY8%|JRm7u~&zELXKTr@>-pyE~P+ksp4ZNJiJ!A)~SX0Br(j8Fuw zbQAlo#CrojIAFGfcn_9X+5UHgw)IplI%3l&S#!e{8?<|~{0#5=VKbUoFV1f1nH@ItKzpbVR zk9LFBNl}E801q;N=(W^mu%>4}6?_9{9M0;BY&!o_?z_V}lU~Ke7QQ7(^g((;*0ipT$}Z z?tB$(zoCVa7$vwBawLEp3CKzef}CIct%02mplO-_XF8+}b`^2fBERQUap+9lt@xE7 z;2@P|>);CB!h2ui_C-Kd+Y9sijYm10`{2*v^Y1nprg(Hg2B|Dg>F#6Q+_ek0_apnbPD; zN|9rfg6@H<>s$Rd(fm9R#dKx>5a}$%Ogs$%e^TIOntzB_2P}`6MHAEg-2gXDaRC0r zs#2nkUMvzr{y?(|dC2W9{1Joq;ZJv07&#a*{)h&^AH7|GHXu^$DmyT50efV?p0f$; zQ99}X?CHU8L=%}W!%-|Fs^IRV0DbPlhRj-eFzx^h$)Rp%;B>M8D)Qsgh&hLhdzN!S zKA?Sa5YmvwPM7>mw}oToMkyfJQO@xp?1|GUg8zt$^HdzLunW{CLRqTXY(UExe+Ny` zn4Rde;GZ1__;M1&47NrGJbsH_-j879Gz4@wXe&QQ$DeQu9Rwr*ASfh&U`(D+3<_mq z{ok8T)^ccopQD**I*3aJMuoWOB-AIY>HjJ$4fu2+)aVw1vPb|OA*emL0f;6;r>Vym z1$5~32*~hdsCTkKu9k<83zTBoQv}+4CTt)KIy@*aWVs^Qd-`u&@DO=y<~z$N|A4Y+ zc2`<9-nB9WH{cGqTNNPE(w3KQuS6;wU{_`t?f}gCf$g$L#pn6Hv|gjn9~NKM(D7&7 zJ^|BMZu>y4pb)JAiu7PeY-1Un$QX$+hi6rg*xhBF17BtWN_f)fHx zxg8|N#0f#JW%w0Qr!xdLPyw>*VAE%SR6y&qeC`F%<+p`6Vjo^uXVyLO^;l zCLt1jnW%TEn5E(h6~qW2?4Iy^D-}!8=U1tpm?aQBpC{@)DtbT(2_FLQ2L&lyq9{(G z*GeU}`xjJv8ZW`xCIHF}KyZy#&f ztI{V6A1gZw9YmSBsrbuyYMToGnCaw+FG=?XxWw5~FJ zmJ9n65)%c9Ey`S~yM6#8Z(aT;%}ty!a4cz`J~+wrrFyeksr#2fbYE$!r(LhDo%USs z8~Gh%zD8(Hjrt>vxz&IZ?BNayBzEQXNNsKfk-Qnn_YeAR8uNGDiSC%tnT(1Qk4m~z zgdUA41svlvP0Ti6?!+9@U?EY;=^#R$A*daQvlx@764MhcFiob!ErQc9JyH)&r#+}T znN+0Vr-X-Ac6E07+We~h{?f{7uX1x~c@;T1Iz`1MGRRPIwD=b~MPgA>G@wP%Hn3vi za&N`r3@O)O*OUgSLEl7jL}Ab&av78Y0fo@RlnyitZcDCF5)LlJHoXz1?ziqCtzw%D z%23wZmsgy?FVX3`^r=W-v$biFX!x{*qhbj$xP)U-bBkA<(uRBA@k)uak5d=z{J=|X z^Ru0j_;2gtv522F^f+-r!b><;^O3mF*;b>4tUXSjjS5sjL~(LQk=gwsSS@%+;lWb8 zdy#zaph{dt7m{LYx>x=Z_nnXI7NP$nBc#T!jw%Ff8>7=Nx$`{8H%YIYVk}lU%XL#J9FGj{Rb5h BV?zJ{ literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/__pycache__/parse.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/__pycache__/parse.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3ecc775440b880af66af1c5f9b73884059ce092e GIT binary patch literal 4130 zcmbVPOOG5^6|P%VU0wZ}=^2m5k3cB$vK#Da2O>vyoG8JuB2Y56GL8hLKvCWIPEYkz zSCwzw9(!8V8zyWbkb=ZALiRF$A{!#amRFDpv0{Tahy^QFeCJlr!-f^rs&ikb?mhQB zzH|D6PN!+$x%#VLgugs%7=NR|$)|(Cn|SlzfN+De*zl{C`Am;Z-$ZN1*2MO0Mr&4F zn>fCMzRhcKed78J-FM>Vq~*7CzaF@iy=9CBDqNe1)%K z?$oDkzsuMB6~P{^^3#u)f9f5Buk$k>8T^b`JFxuI{478Bk>RiN^ZXgKXZUmcEBvb; zG5;)omVXU1=eWnej`lofTSo8s$GD5J*<)qoDp5L7k_)LyR|>Ryf|s>>Qz7?#mp+`N z5-eQALJ7aU8^v)joXR|vd08K(lS!Cxzq9BBdiP%YF@reK-yu}=yE@44iTr`cABp^l z$d^R^Or#|87b1TpVi;XxR=W}58Pc6kJ`M(N;?1vs2!pH%OETZ$Cb!6P+~M_+?@a;5A{vFH}U2dK?+tFfl*kXtS~{% z$N;r8wK?@`+NqOEVDbXNEk3=HGk$Oh$XK za(^$B{p~d7LgxM1%l&JEy=u&x$}J4^&7Yd*=*)J}#Odt=#+@^UUT@IoL+mqe zfUiz-1yfDP^`&Yltj#;Bqn5_Y$NLl=?*81w%9839OND!YbCb4O;f=!O%>xTkw8pFX zKZ-`t3RaVkAyaGD{DjHbkyNK>H;f>~Lb7EV>C=~?72-}RInev@|1ZgrT5}LQN$w`So?)&$YJSiF2clH&5=wt5Rmp|?3ZHBEMt^nT){8ScTwb??Or z)*e$>%4Lwp2gInmjKHPM+Gdm4tc|{n*FoR4T-Ie> zyKREUaoKs+w%V2h{-)_#Ys{@RSra3gU$WZxrp@GQ*z*a1B5*nas2^d#2@u%aB%q=U zjnJ_QOTMI7VQH|yT5?ve+IqF(tm!^e)d(5=z(S~*_}T{kTZJ9ig^h4?XIK9#-1NP9 z6?F^avXNLu-W*`+}xC}Lcq-vc~z%bollpJaDw-}I)9L!Mx6w3m%Ogm zzfcZ1>ICdqt8@2;k-V?D8sPd=@7YA^a0*9$Q8;uePHEwkTHq!ZHD?=~Uue!wm1Hog zKgn6tV6&y!507lN$ljjX36-}K=H9}1WH*U|b1zJk$6<*m_E4UIZ(&c3Rfpvn%3qHH zVMEVuY6zQIFQ6`~BS-;xe71ZDhl>eG{~!FuA&Ta+l|zQ3DcJx^S+7duykz$z{j2cn zRUw@B8l@daxhiV_iAi3$$V=d;$}{at&(ZL>P;;erAzh+T3O`ZTY2SGhb0}OzoImA8 zvX7Nd@#b`Z;lO!x_^!i|mJrG=TLVQ%o2!eqYwB;F2u?zFLOMEH7i0;7OtV1NTa^HYFpOZ}5|4m8bCj_VYqiXOK3<^VDp?El84^jAV6QvHYqF^XS zOnBiXg$O8&b*%pcr4fo7!iw7L^&`Ji(=0~j-S#f>!6hWPI2J>y{Cme&yP$Jt9ACg6 z1iH#b$u@M}o4s(9P>_X1j$Dl$68LE1WkN7MC>x59}*LbVK*|e=L5QEIF@reEvJR{$x?VKt}N~*H`RZ3pS_$Tu*DbIKH z0v4-OXCc!`LKpcmtu9BXBV-cBR4inn(n6n@t^PSm{53P=Gej;Bd6CE^kkZ-GiCR9d z2kBr8WmfS(2|{C=_C{^l3Q+$?5nQ@As(Shja@3r%7E%$?5`iRLzHPaV$^Pf>UryIH Qn^t$lz2mMnpL1*f0kzZst^fc4 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0c2ce919cd985baccc85b2691a5fd66756cea18d GIT binary patch literal 2469 zcmbVOOK%iM5bmDmzU?)B@m?ND7)9(FlAs_(C_GFQMJ$9hg!E-J-fl0m%+8K_+BRN| zPv+{N`~hT?6Mw0%Iq?@bB&qHh+lvI3%&M!as$W%KRaftmdcA_+yYc4FfRz#YTM5&j z1HzBc(hoo=qBue%?=it38<8ycImFSt9l4_tDFg3NH>!-Pq^kK+ zR2$VvUGrt&8)QcFm1uU)}w{dB3aaY1NbGfgb}k2mgx*T$!3P9 z=C(QT=5AoROi#YTdzhT3r|4;z(V}PQ zS?Dh?G`vX9(etkmj9#L+hrHH%@E>(OEX_w@44u;-#~a}QxT@empZS|2j8YI6wt0~F z8=G-ogyYy(Qo{Iq6PCkhv-jwB*MG2jyZ7Yr>OKFL)!#u`?j=FIwv#Zm-X4wP!5Ekd zWeI2fKrjlDvl*+c-s57QHFYR`pyu9p1yY*6z!iKLyz_3OTm`)1+JM@g)d{ zmSKJpq{-zcDQoEi!g$b9uCsu)uB42$#BtG#gwmMRjLXOGD>(3^A>QQ56m4H z6_B75Q)AEMvlR7^Ftn`GRhYsW+SJ;|2ado)cUV$hi88gRBPxdp?YUjq@M$0dIv)5M z=KE_NCMMk4Nu$W0wgQ0hNwcT>_5iL~I_|$(%>Z+xCJdO0YSOJ23Oj? zzI;jxp9@X7qWfev!>5Bz#@*Us#xP@aB6-=Sa8 zWAv9cTyf7(W)01u1x3p~a577%xErE8tWaa$eEt2dbF|CFU0YN$C$on&<$C5E)I~#Q z{zrR1J38fA4v*QL%pE4i6hKCv_2m?5xi6rYdo%eBvHguOro?4&upTk0Z-}(>tE6xs z)i6FP%3R&n9A$+XaUVSF{`BzP9o_&*x2uK>9ZEmf(Cj2eWjh8;X_IM_fnWc}+HhSpWE zp6gwf6;*24OA&?Z+G^q!wOISEy&*G&s$G<2uDzZ=)NZkuj(J;oAh-<&^RIx&GR%fD zAB$8wDNQ(qD@-{2PeJ?=5Yp6-Zu{_|glflCCXq(2FW%miSJetn5moy0>ZJfBmU=Fw zMZ><}m(@^Hk!3~Hv!E-mu0#5k>D-l#!sSsAL!IRsi76y?ozo?*^h5p_-coONdILzw kC>5yI4ttGf&9txqKkHxXAFJ$`CAVZ(@T^m+l}Z-=4>+= 3.12 + if VERSION >= (3, 12, 0): + + def run_operation(self, sock_info, operation, *args, **kwargs): + with self._datadog_trace_operation(operation) as span: + result = self.__wrapped__.run_operation(sock_info, operation, *args, **kwargs) + if result and result.address: + set_address_tags(span, result.address) + return result + + # Pymongo >= 3.9, <3.12 + elif (3, 9, 0) <= VERSION < (3, 12, 0): + + def run_operation_with_response(self, sock_info, operation, *args, **kwargs): + with self._datadog_trace_operation(operation) as span: + result = self.__wrapped__.run_operation_with_response(sock_info, operation, *args, **kwargs) + if result and result.address: + set_address_tags(span, result.address) + return result + + # Pymongo < 3.9 + else: + + def send_message_with_response(self, operation, *args, **kwargs): + with self._datadog_trace_operation(operation) as span: + result = self.__wrapped__.send_message_with_response(operation, *args, **kwargs) + if result and result.address: + set_address_tags(span, result.address) + return result + + @contextlib.contextmanager + def get_socket(self, *args, **kwargs): + with self.__wrapped__.get_socket(*args, **kwargs) as s: + if not isinstance(s, TracedSocket): + s = TracedSocket(s) + ddtrace.Pin.get_from(self).onto(s) + yield s + + @staticmethod + def _is_query(op): + # NOTE: _Query should always have a spec field + return hasattr(op, "spec") + + +class TracedSocket(ObjectProxy): + def __init__(self, socket): + super(TracedSocket, self).__init__(socket) + + def command(self, dbname, spec, *args, **kwargs): + cmd = None + try: + cmd = parse_spec(spec, dbname) + except Exception: + log.exception("error parsing spec. skipping trace") + + pin = ddtrace.Pin.get_from(self) + # skip tracing if we don't have a piece of data we need + if not dbname or not cmd or not pin or not pin.enabled(): + return self.__wrapped__.command(dbname, spec, *args, **kwargs) + + cmd.db = dbname + with self.__trace(cmd): + return self.__wrapped__.command(dbname, spec, *args, **kwargs) + + def write_command(self, request_id, msg): + cmd = None + try: + cmd = parse_msg(msg) + except Exception: + log.exception("error parsing msg") + + pin = ddtrace.Pin.get_from(self) + # if we couldn't parse it, don't try to trace it. + if not cmd or not pin or not pin.enabled(): + return self.__wrapped__.write_command(request_id, msg) + + with self.__trace(cmd) as s: + result = self.__wrapped__.write_command(request_id, msg) + if result: + s.set_metric(mongox.ROWS, result.get("n", -1)) + return result + + def __trace(self, cmd): + pin = ddtrace.Pin.get_from(self) + s = pin.tracer.trace("pymongo.cmd", span_type=SpanTypes.MONGODB, service=pin.service) + + s.set_tag(SPAN_MEASURED_KEY) + if cmd.db: + s.set_tag(mongox.DB, cmd.db) + if cmd: + s.set_tag(mongox.COLLECTION, cmd.coll) + s.set_tags(cmd.tags) + s.set_metrics(cmd.metrics) + + # set `mongodb.query` tag and resource for span + _set_query_metadata(s, cmd) + + # set analytics sample rate + s.set_tag(ANALYTICS_SAMPLE_RATE_KEY, config.pymongo.get_analytics_sample_rate()) + + if self.address: + set_address_tags(s, self.address) + return s + + +def normalize_filter(f=None): + if f is None: + return {} + elif isinstance(f, list): + # normalize lists of filters + # e.g. {$or: [ { age: { $lt: 30 } }, { type: 1 } ]} + return [normalize_filter(s) for s in f] + elif isinstance(f, dict): + # normalize dicts of filters + # {$or: [ { age: { $lt: 30 } }, { type: 1 } ]}) + out = {} + for k, v in iteritems(f): + if k == "$in" or k == "$nin": + # special case $in queries so we don't loop over lists. + out[k] = "?" + elif isinstance(v, list) or isinstance(v, dict): + # RECURSION ALERT: needs to move to the agent + out[k] = normalize_filter(v) + else: + # NOTE: this shouldn't happen, but let's have a safeguard. + out[k] = "?" + return out + else: + # FIXME[matt] unexpected type. not sure this should ever happen, but at + # least it won't crash. + return {} + + +def set_address_tags(span, address): + # the address is only set after the cursor is done. + if address: + span.set_tag(netx.TARGET_HOST, address[0]) + span.set_tag(netx.TARGET_PORT, address[1]) + + +def _set_query_metadata(span, cmd): + """Sets span `mongodb.query` tag and resource given command query""" + if cmd.query: + nq = normalize_filter(cmd.query) + span.set_tag("mongodb.query", nq) + # needed to dump json so we don't get unicode + # dict keys like {u'foo':'bar'} + q = json.dumps(nq) + span.resource = "{} {} {}".format(cmd.name, cmd.coll, q) + else: + span.resource = "{} {}".format(cmd.name, cmd.coll) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/parse.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/parse.py new file mode 100644 index 000000000..1a4330d2e --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/parse.py @@ -0,0 +1,204 @@ +import ctypes +import struct + +# 3p +import bson +from bson.codec_options import CodecOptions +from bson.son import SON + +# project +from ...ext import net as netx +from ...internal.compat import to_unicode +from ...internal.logger import get_logger + + +log = get_logger(__name__) + + +# MongoDB wire protocol commands +# http://docs.mongodb.com/manual/reference/mongodb-wire-protocol +OP_CODES = { + 1: "reply", + 1000: "msg", # DEV: 1000 was deprecated at some point, use 2013 instead + 2001: "update", + 2002: "insert", + 2003: "reserved", + 2004: "query", + 2005: "get_more", + 2006: "delete", + 2007: "kill_cursors", + 2010: "command", + 2011: "command_reply", + 2013: "msg", +} + +# The maximum message length we'll try to parse +MAX_MSG_PARSE_LEN = 1024 * 1024 + +header_struct = struct.Struct("= 3.1 stores the db and coll separately + coll = getattr(query, "coll", None) + db = getattr(query, "db", None) + + # pymongo < 3.1 _Query does not have a name field, so default to 'query' + cmd = Command(getattr(query, "name", "query"), db, coll) + cmd.query = query.spec + return cmd + + +def parse_spec(spec, db=None): + """Return a Command that has parsed the relevant detail for the given + pymongo SON spec. + """ + + # the first element is the command and collection + items = list(spec.items()) + if not items: + return None + name, coll = items[0] + cmd = Command(name, db or spec.get("$db"), coll) + + if "ordered" in spec: # in insert and update + cmd.tags["mongodb.ordered"] = spec["ordered"] + + if cmd.name == "insert": + if "documents" in spec: + cmd.metrics["mongodb.documents"] = len(spec["documents"]) + + elif cmd.name == "update": + updates = spec.get("updates") + if updates: + # FIXME[matt] is there ever more than one here? + cmd.query = updates[0].get("q") + + elif cmd.name == "delete": + dels = spec.get("deletes") + if dels: + # FIXME[matt] is there ever more than one here? + cmd.query = dels[0].get("q") + + return cmd + + +def _cstring(raw): + """Return the first null terminated cstring from the buffer.""" + return ctypes.create_string_buffer(raw).value + + +def _split_namespace(ns): + """Return a tuple of (db, collection) from the 'db.coll' string.""" + if ns: + # NOTE[matt] ns is unicode or bytes depending on the client version + # so force cast to unicode + split = to_unicode(ns).split(".", 1) + if len(split) == 1: + raise Exception("namespace doesn't contain period: %s" % ns) + return split + return (None, None) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/patch.py new file mode 100644 index 000000000..5aeaec674 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymongo/patch.py @@ -0,0 +1,84 @@ +import contextlib + +import pymongo + +from ddtrace import Pin +from ddtrace import config +from ddtrace.contrib import trace_utils +from ddtrace.vendor.wrapt import wrap_function_wrapper as _w + +from ...constants import SPAN_MEASURED_KEY +from ...ext import SpanTypes +from ...ext import mongo as mongox +from ...utils.deprecation import deprecated +from ...utils.wrappers import unwrap as _u +from .client import TracedMongoClient +from .client import set_address_tags + + +config._add( + "pymongo", + dict( + _default_service=mongox.SERVICE, + ), +) + + +# Original Client class +_MongoClient = pymongo.MongoClient + + +def patch(): + patch_pymongo_module() + # We should progressively get rid of TracedMongoClient. We now try to + # wrap methods individually. cf #1501 + setattr(pymongo, "MongoClient", TracedMongoClient) + + +def unpatch(): + unpatch_pymongo_module() + setattr(pymongo, "MongoClient", _MongoClient) + + +@deprecated(message="Use patching instead (see the docs).", version="1.0.0") +def trace_mongo_client(client, tracer, service=mongox.SERVICE): + traced_client = TracedMongoClient(client) + Pin(service=service, tracer=tracer).onto(traced_client) + return traced_client + + +def patch_pymongo_module(): + if getattr(pymongo, "_datadog_patch", False): + return + setattr(pymongo, "_datadog_patch", True) + Pin(app=mongox.SERVICE).onto(pymongo.server.Server) + + # Whenever a pymongo command is invoked, the lib either: + # - Creates a new socket & performs a TCP handshake + # - Grabs a socket already initialized before + _w("pymongo.server", "Server.get_socket", traced_get_socket) + + +def unpatch_pymongo_module(): + if not getattr(pymongo, "_datadog_patch", False): + return + setattr(pymongo, "_datadog_patch", False) + + _u(pymongo.server.Server, "get_socket") + + +@contextlib.contextmanager +def traced_get_socket(wrapped, instance, args, kwargs): + pin = Pin._find(wrapped, instance) + if not pin or not pin.enabled(): + with wrapped(*args, **kwargs) as sock_info: + yield sock_info + return + + with pin.tracer.trace( + "pymongo.get_socket", service=trace_utils.int_service(pin, config.pymongo), span_type=SpanTypes.MONGODB + ) as span: + with wrapped(*args, **kwargs) as sock_info: + set_address_tags(span, sock_info.address) + span.set_tag(SPAN_MEASURED_KEY) + yield sock_info diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymysql/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymysql/__init__.py new file mode 100644 index 000000000..184952e31 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymysql/__init__.py @@ -0,0 +1,68 @@ +""" +The pymysql integration instruments the pymysql library to trace MySQL queries. + + +Enabling +~~~~~~~~ + +The integration is enabled automatically when using +:ref:`ddtrace-run` or :ref:`patch_all()`. + +Or use :ref:`patch()` to manually enable the integration:: + + from ddtrace import patch + patch(pymysql=True) + + +Global Configuration +~~~~~~~~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.pymysql["service"] + + The service name reported by default for pymysql spans. + + This option can also be set with the ``DD_PYMYSQL_SERVICE`` environment + variable. + + Default: ``"mysql"`` + +.. py:data:: ddtrace.config.pymysql["trace_fetch_methods"] + + Whether or not to trace fetch methods. + + Can also configured via the ``DD_PYMYSQL_TRACE_FETCH_METHODS`` environment variable. + + Default: ``False`` + + +Instance Configuration +~~~~~~~~~~~~~~~~~~~~~~ + +To configure the integration on an per-connection basis use the +``Pin`` API:: + + from ddtrace import Pin + from pymysql import connect + + # This will report a span with the default settings + conn = connect(user="alice", password="b0b", host="localhost", port=3306, database="test") + + # Use a pin to override the service name for this connection. + Pin.override(conn, service="pymysql-users") + + + cursor = conn.cursor() + cursor.execute("SELECT 6*7 AS the_answer;") +""" + +from ...utils.importlib import require_modules + + +required_modules = ["pymysql"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + from .tracers import get_traced_pymysql_connection + + __all__ = ["get_traced_pymysql_connection", "patch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymysql/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pymysql/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3ea73cbcb15f8711ec5594f56626d2c5f180187d GIT binary patch literal 1844 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 or :ref:`patch_all()`. + +Or use :ref:`patch()` to manually enable the integration:: + + import pynamodb + from ddtrace import patch, config + patch(pynamodb=True) + +Global Configuration +~~~~~~~~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.pynamodb["service"] + + The service name reported by default for the PynamoDB instance. + + This option can also be set with the ``DD_PYNAMODB_SERVICE`` environment + variable. + + Default: ``"pynamodb"`` + +""" + + +from ...utils.importlib import require_modules + + +required_modules = ["pynamodb.connection.base"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + + __all__ = ["patch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pynamodb/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pynamodb/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a95fb60938e699e4301cafaa6c83e6f30fdc1033 GIT binary patch literal 1121 zcmZ`&%TC)s6dmV5s0E_SE-H1D6_H9zUr4biDiBn$C{RdWt}1)%nIwbk8FxGmO%~ns z1FHT&_xuvuuK0znddH4KHXX_HypMCwxqi34ZX*~wzkiYIIzoTUVR_tx!z=i*K1>vO zh+4E3TS?8US%}t0mRDyrR{vrBeCRb;Bio@3+Kih?(`&-}3cRm;Krhf+bdEkE+PXlU zRdQWJ$R3OtKAQ@X$o>K5LbH(~noEJTA`#0liDOJd9Ko95giwaBxE^B-ZfW60Lp$c7 zB5DekO6H^TF_J>CsED+GcHFh?BSFHLi;?|(KJ5SJ<{4(joKZ}2EfYXw08H`KmbNCZ+3PU`@H}Vohk^$mdz>?@YfQm6?u`h z#8T9}#E|RSHip5IR4RSPhPfLmncz8|g@FO`7y~gwJ}Nv5IGYuXF9#}T9ov2#%aFwQ zu&~QZH5Q~*d!XwuHQs6a$&u=iu&ah$zKTsV` zF=a!N#~KfTJ|po`XJnd)hyiU--e3$0$+S>6A_AHulQ=YS^<8@eLBH>xeLC5HbJ{=f zFOJUNA0HkC0Vq4+N{WODZDLJ`;zsH+a=!%N;vi^O6|{rEwr}CDKppF5L$R+pSIkc& z&106$9#m$g@OjI1Lz1yutMq6VG^+-jPCBidmhV#;`M$T2YaVCaQa;nd@H-&y{yaXd z5_lU4&rHWv7eyo!271HyuaRnjUoriiIjQ0kqL~9#IYSv!rZQ*t#Oa0N>T-MM@~L|A zOr+cXw~^@eX3w|H7^Tj@NqM@(1#le4Ta#JZn@$tv%o}L2LV;TG(5WhRe4h)heILZW aS=}ue*)wHlYcMq)t<_g*w$-rKYX1PkB51P! literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pynamodb/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pynamodb/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1128365fd82cff1f231f572ad6ef8db85384dd5e GIT binary patch literal 2382 zcmZ`)TW{P%6rQoYw%2>fCQXy3rDdNGE75EVrBV^92yH45Z4{cV+RlsR>`WGKd}BPD zG%N2@crBNAR3+kxKfxnE!VkRg)W1*($~m)}G!$XWXTCXeJC`}1A2k{tf#>FnpMpz( z{>0ANrvjbN;LCpp!3iUrawDS5fYyx6*kTq%ofTDLo7sRX+>V^sWiDWcyHPdvn5S_S zaE;Y8_M&>+U=59HQ8S)na~jv9Ry@zIqjT{hThw?iIv+2wB}#-bTpnH+ zuCR;3I=aMLe4e*LdThX~1%B>3!rtMF{5-Um`4V4-_6j$)Naw=%6R#(OzG#0t5;7Ed zTcz#oRHgk?igu8NZJs8gy%z@Upq(8gL7ejKc0US5qP&+72eQ$jrQJ`Hop1oCvM+;7 zy)<9|dNqYx`y&-b`Ac)ETUpy!yZ^NJ<((~mYwf}2{dNE0T5sL|YW*oJnBUr5+wdQ( zuWdbgxPEu0tZrpN(mTjR4l~TnFoCAACI_P!j98a4m7rJ{2;~PerN0+MBLUhv7b5dv zC{PM#*`ovvEZq}VSj*>PCm2P_&xPCz`=XCdoqaIvci_wKgHS}#p)oWu^@pZhr`hRnv|%@kId zhs2Dq^B53+XO!rZeC%aHmMv|M;HPIuEDmO+iAqcFjU~Slc>xBrc(vcnWVjb7(cKGV zcPEXwka>4}y?bjr*&klJIs8yw|0v0>`OgN)ty|+8*YFu+-Cj2fv#Vi}D{$uO-Z;B; za1e_w=lcGfw&019;db{VLS1d+YIaaoG-`t(xvpW_q84o#O)4*f?msEDK&exD1HfA; zy>=#@kYY^dEO9nB53!ywb=HP#!PyC8*8nBUVa zQdmQCXmN`7vapmhbh&YCs_M|==5zChKPHu;a#T}wyqDviYT!D&zlYYuF6^S>Hw%N? zD@4BF&J^w1^2HQ!XN620T+iLYLAog_!zQmTlfr=y);=c56^p33T~Gc-3BKGzJtq_U zDM^ecM70XaYlp@J?1J-LUjNygRAJBgqKY=5#lr>O;LYcB;uRhzyVW15w6xXd3QwOp z^LA1zYLAIQJ|w)`^k8DN|38D6xym3gOd$9=C~T{ z=c~a0m}}fXczVLv!|rr)uS41VC>Q=9O1Fc^hl`K&x;U*t7pZPXrZICwf(CGA>q3!b zW!fTTv6^^xN@@}bd3q_UfJ*C&UT>2j5%?+?NEb)(UX`vCc{-APA#12>fMD)yTFzGR zA=iRoi$P!4nmlr8Krb}O(%U(TENYDY47gq*381Bv@qJN zdKv+F8BeR7iC1Z11X=COL8T2A0$rb}AuJ!c1fmOHej5ZaJX)uoVN-)NiS^nx48t=F z+N8Fz2qSg62;5-949_$uynoph&uro` or :ref:`patch_all()`. + +Or use :ref:`patch()` to manually enable the integration:: + + from ddtrace import patch + patch(pyodbc=True) + + +Global Configuration +~~~~~~~~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.pyodbc["service"] + + The service name reported by default for pyodbc spans. + + This option can also be set with the ``DD_PYODBC_SERVICE`` environment + variable. + + Default: ``"pyodbc"`` + +.. py:data:: ddtrace.config.pyodbc["trace_fetch_methods"] + + Whether or not to trace fetch methods. + + Can also configured via the ``DD_PYODBC_TRACE_FETCH_METHODS`` environment variable. + + Default: ``False`` + + +Instance Configuration +~~~~~~~~~~~~~~~~~~~~~~ + +To configure the integration on an per-connection basis use the +``Pin`` API:: + + from ddtrace import Pin + import pyodbc + + # This will report a span with the default settings + db = pyodbc.connect("") + + # Use a pin to override the service name for the connection. + Pin.override(db, service='pyodbc-users') + + cursor = db.cursor() + cursor.execute("select * from users where id = 1") +""" +from ...utils.importlib import require_modules + + +required_modules = ["pyodbc"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + + __all__ = ["patch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyodbc/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyodbc/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5138a3dd78efbcd5f094b7a58a31a51f20d28af9 GIT binary patch literal 1706 zcma)6&2rl|5T<^ea-4A=pa+a{h}5Y_yFboYV|$WHO5;Oo*H)TVnGOjOkc12gFaRi9 zKJ?ND==248?<@J*Q?5Ps)CEAvc02vSj7UIS?C!VUxBK$pL*Ik7|I5$#_m=1VW)64P zdvJIPKQ)2}@unWBk$P6k>(hG8BaL}&+MspX__6lW2h%2Ps(sQVt*n)|rY-p1hVSiH z-e=y|-nsX!N474!-UHl*K>lPwQLz#vNfG0k&L!4NaM&v?%baqpkiIp^SR%1pAuW)W zINe-*TT;oW3Vh!mbDU(1&;1{&<=YtdoGV0)6(tClTI3Ki#aXsOmkY{Ksm$$xq_aUx zhz--1B|oh8usx2EkjPmSSf`5!Zg+c!oBeSNnVd>+MemxeP2fHN$T2T%RF{i|c_+)k z!1ocXnG`vyVk4FpLTY3K*{^ojbp{Y_k#ec zKOk7+!C)OFNbSYId41hcR4!RcJKx)c3}Mwjh~u0hNli{r%VdQJo#8UmXa@DESXBk) zh8Tb{Sulk}VF^w#M>tagCB~gbmrO5g3F3G(iq5_}9eqBGF2?8IoD9ct416t_6x;~H zcv@n~41vz?$VDBXIPSQpojCq~*et!#j2aSisuzN&3g@c@9H}%+bD?h)XKhhs>*5dB zSX|*s3B_14{5Lq0^Jl|x^n5%SzKFgYPhOmkF78A7XI#&rR@8vTxYl+tE(Ij~wrp zEXyjbAdHMEZaH7e285uY`IL1<5_D9F$SBgJ*XTwk=Eu{iSM~EbW+jo1` zjXMPNil(KeU1*;SQb!-UPPRVGppg_Y0@wE-|91_Ts=eCHj-+o(CTWxlQf5?5+b;6E zn)}|e?Jk>e`l`2evlT@|q){~8DK*Pf;EHc%WF0J~?^oW)#@$ZNl$mbpKddL?2If!{ z{p!grxMv@KT!n>XORQ-Ky%x?yMgV(wy%!!N{PNB2{+o~G-Y2}+jjrbW;NbexU9;vz zI0*|@^v(DNpZ(=^aj;tDG^_^L<_)z6_L9)y3!{i}rlSaE&dq~EhyK`rR@?AsKG|-x L>wc|S+phl!7KbcL literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyodbc/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyodbc/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3127d237bd48439b8cff019abec0337dc6625804 GIT binary patch literal 1954 zcmZ`(OK;mo5Z)!15+zHq{7jlA4Vv`DqOqJN2w_e!V0Wn z-+?t)$9@TJ!Y%BVe_-$d+&*P+TQ(+cxPs#k;SP@PNFT?mz+bT7?m6bfdgm_cfLHGG zG{>%%6nQ@#kQM8_qR7yy52T4?esJzgw{~__oJiO%@?0h+E$F}2Y;L4oEgz=2HgT3cJUA}5j*f=13t*wT=+UK7>0WmV zWY@;MQ65#Ewbn5V?W1D6#RZC@ng0jvGhDp_+Wi?|2p|E5;JFhb8o?!cY7uwus1E8t zs3q(yDr%W5s-R00rq&wHRrOJBL%WLuT|-5oVPH4WO6d0H4J(fk3+-*vnPWC~#_n(Y zL>M*}6JjgxAxWC{JPSVFkm!%{r1C9rc_J$jtAVa+KOfp&&=p;Yi$YQ>s2-qb>z+5c zok=)O%^L-BL9HoDRQTYWIVaq33=OeC+ZprM&e(awIR-mLi715pt+6H6?fvk9fuU2rsy&jnI9C*oOk)^wd4v)VszL`k#12NNT#8v8#Q< zPLQs~8MBGA?~eJ{F=F2vyTH%HjKGfNPGXacl1v9R8(-z=Q7M&mM2V+)YNAMy7fbIz zR6@(Fe|ahDQ(Sw?t44&!#9HI-O9(#4wI?Vustc^b<9pXozIZP;=FS4A*TYorlgkIB zJ|RUXRqdINL}uCCk98K%77IEd^cZChI*o(1>4U{A-)u5rdt}l~Z}bZ_jEz=QZi;XW zJ=lxORME4c8Z@ymeVy(8X<^fQ7m6qbs05^mvCoPvVOu(ucot0y<9~6Z^1qo9>09EX e{t}h%&_i(La|L(o*Lf%2iH2YIYrf~#JK`Tm*`K8V literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyodbc/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyodbc/patch.py new file mode 100644 index 000000000..872a1df84 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyodbc/patch.py @@ -0,0 +1,55 @@ +import pyodbc + +from ... import Pin +from ... import config +from ...utils.formats import asbool +from ...utils.formats import get_env +from ..dbapi import TracedConnection +from ..dbapi import TracedCursor +from ..trace_utils import unwrap +from ..trace_utils import wrap + + +config._add( + "pyodbc", + dict( + _default_service="pyodbc", + trace_fetch_methods=asbool(get_env("pyodbc", "trace_fetch_methods", default=False)), + ), +) + + +def patch(): + if getattr(pyodbc, "_datadog_patch", False): + return + setattr(pyodbc, "_datadog_patch", True) + wrap("pyodbc", "connect", _connect) + + +def unpatch(): + if getattr(pyodbc, "_datadog_patch", False): + setattr(pyodbc, "_datadog_patch", False) + unwrap(pyodbc, "connect") + + +def _connect(func, instance, args, kwargs): + conn = func(*args, **kwargs) + return patch_conn(conn) + + +def patch_conn(conn): + pin = Pin(service=None, app="pyodbc") + wrapped = PyODBCTracedConnection(conn, pin=pin) + pin.onto(wrapped) + return wrapped + + +class PyODBCTracedCursor(TracedCursor): + pass + + +class PyODBCTracedConnection(TracedConnection): + def __init__(self, conn, pin=None, cursor_cls=None): + if not cursor_cls: + cursor_cls = PyODBCTracedCursor + super(PyODBCTracedConnection, self).__init__(conn, pin, config.pyodbc, cursor_cls=cursor_cls) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/__init__.py new file mode 100644 index 000000000..5038a837a --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/__init__.py @@ -0,0 +1,60 @@ +r"""To trace requests from a Pyramid application, trace your application +config:: + + + from pyramid.config import Configurator + from ddtrace.contrib.pyramid import trace_pyramid + + settings = { + 'datadog_trace_service' : 'my-web-app-name', + } + + config = Configurator(settings=settings) + trace_pyramid(config) + + # use your config as normal. + config.add_route('index', '/') + +Available settings are: + +* ``datadog_trace_service``: change the `pyramid` service name +* ``datadog_trace_enabled``: sets if the Tracer is enabled or not +* ``datadog_distributed_tracing``: set it to ``False`` to disable Distributed Tracing + +If you use the ``pyramid.tweens`` settings value to set the tweens for your +application, you need to add ``ddtrace.contrib.pyramid:trace_tween_factory`` +explicitly to the list. For example:: + + settings = { + 'datadog_trace_service' : 'my-web-app-name', + 'pyramid.tweens', 'your_tween_no_1\\nyour_tween_no_2\\nddtrace.contrib.pyramid:trace_tween_factory', + } + + config = Configurator(settings=settings) + trace_pyramid(config) + + # use your config as normal. + config.add_route('index', '/') + +:ref:`All HTTP tags ` are supported for this integration. + +""" + +from ...utils.importlib import require_modules + + +required_modules = ["pyramid"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + from .trace import includeme + from .trace import trace_pyramid + from .trace import trace_tween_factory + + __all__ = [ + "patch", + "trace_pyramid", + "trace_tween_factory", + "includeme", + ] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..601bef599885f2ddbace9228a97b218193bdc191 GIT binary patch literal 1798 zcmd5+&2Aev5Z={~Wv%VHKyN(_kb|ti(%MNIz(O1(XkoOMB5hT)SOnlwBDG?`cYDHo#1;mkMR42SdG!9f(@c=GEn^!J@0 z_|t9PAAQ_B$DdI+u^)mA6uXl0XPx`p-vxx1n{j8Hj-v^%tUkBd=KLqUH+hF(s{i72E(*mrf za}JvSSaD+wEOe0rg_mnhbHRX?r4(~&MWK!x)pb$n_vO*NPz#YwCQ%dt4v$dQ_T%~t zL|zu!!e?Hy($p6EPM0yy!RcBpX5+?oW8@D>BdlE*Zmm$6fpd7{ah%@)qn5HFBej9y zdL`z3023JG>(M2jjWC#zqB$QNd!x5CSVQrAi`~)2#rbA2^fp^$j%w3kjq?C1!@*~# zfusghp>rz7-nN#GDPu$zmE}hRp%}j!9K#?UAjpdq6_U;*hmB95nqyiYLYm$iT$)Z` zzN9MSV3!=yCP^s*_~5}sdyhF+&JAAd<1r5SLqX31*2<#RD6UN;V@@GEN zcn=K@2n)7At`}4qo~CYt+CHMs@2tSNL8~bGa^ceD)8^@K_>b)+=gJ_$Z6a1wRv2XA zB%W)n0t;m5QXXv;szX(rGiQM5cImsn#wPWBdN8t}bF9mCnnwJ}RlTrs?GPL{iTuZK zf!_EP%}dE$xx1>nW%!>;(bw8n?2Tk;4c|01&gY28mWUrf>$ zQoA037_ zhpw9m%}HLcN^+C*n$*4vYlUt}?R<%veWB*EVm#-#IoNvKDER1}>d@FV1C8B0&^~z8!^#S2#&yS&Vy0WDk$l)tVz-Yo33~1QSY2Ma4IDs zzXf{FId#)t>$ucnMJl76|oZ6(%xdu7d>Wqqc}PfIZCCb_1PIIxHXIg*1s$wPq@ zph${PA|=@Ht?DM(2Np4yj%cLWkW<6;TnstcS%dZOGA`L5r)?l4{jTi-3$plr;R;wunth% zb6Q>8A(ag8xPIWdt$u(zG!-TN$Wg{RJ*WE^xNSdEp8mu6&fR?%Q_l(TyQS0BF5TAx zDJ|y7v}n#WdYXp7wLyypvIp{cTyKugb>l*+I(->Q+g@DOQyVE1ASF~yNE5Lztj!n7 wj^hbO0Lur+;&3X{g(4MeHW7f5JWO@>uNHES;()`aZLIdin-PK2DwF~YXATM literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d02ca4475882cc5c3f6fdca182b237cb82aec70a GIT binary patch literal 2243 zcmaJ>UvC>l5Wn5~>$81vlDcV16;)MLFsN}`P^Cx}s_PgCYD^TnExni1#k+RTIiF8^ zYp03MPl3lE@dZ%wScz}J3*sZ(D^K|f2vKI&j++8vt=*lSnf2|=?>F^1?zf;^Yxk^@Gze=u=RoGVuYAKcL=Mat3tf0*345Mz) z14%z%e$3BRFbKV$>S=*Kh&ioGb;zQ;S0+N{*!ZHaG&Vs!Wr^FLJLDXx%fglL&`lFCn5LVR%OM4TGTI?UuDEL`EJkGV?FvC5VRZYf-wY1CsCDO~Rx6D5_fb5m%9 zPsKR;#J&k*M||Pt?1k&oe6QDK+DFXiLnghxXts}>_nnT)m%Y|KFNgx}c@;}y;&aZJ z1J7d^mdt__?bO=!uQD^CTp9(GZdt(Ubw^Q$2gAq{rg&~*eN z7oz}z1ydsQ8wja-3Z)q+XLur!A(*HqXrcpVm}tj%VsNdmpBh9@4e&8jlPLQ+_Lzfo zj3%b^GoU=x6Ln(Ee67?1-yFA6o#y~=yPxNU)a1o97ibeZwNIT?J41l~LTVqw4zDms zj=(FDpNUQKVQH{9fl^QNq(F)%C{;=Bk&@c5BiNM`r|J*l{!)s>eWCzsoMvVLfEUhdYmw>JM>kXiZNwZtF9A@zW9wC&#c zpSzdJ6$;Dm?E5{+X2tJx09?wTYNYyHP!H&%hjdKC%5tX0!&vgsEqJ1DB$SPV4rPXj z0S}YRgiaE$VU($0W;&=@UMvFgX}F?3nLwB2V9AV845do5tH3PExW^ADjl7`iMU)cA zLgAujfIhF-nz#pjT&WZ?bINs+S(21dhi013dP!#NABde@2R|38MLy9j1Jk{C>6&Gk zeIT4x-?v>#B@={oooPFo<0hXWs!5gAiY0xGCBa*%~AV7Y^_f)Q2> zm}19&YB^iAbnwCswvdhF{f>a)Vu~06V|Ot5PehDFSarXfC`YTear9rX`U{ z&fmhwkXrV4I&~@&Wx$IYc{^EKi_J+sHhSSRuz6~KweKXCvzy% zcLxmLz)VETQ;@A)K^n9O3oT(LY?W*57HsZ6dZ_%GYtFjRl$y@$v_7U088RmOWO2lU zFj?;o*}&(?l$nyXIp|o>(;`sLSvjSvAa15h=w*D8c&_I8gk%~CI-D(ohiqRGJ^9W{ tam|CvtVe?(8HMz7fp79LEEcw~03YV%pQ<6C2P(bQ{?bfMwF=gUe*;Q~HT3`h literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/__pycache__/trace.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/__pycache__/trace.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6c8d35ec78933aa2b1d6c9cc85aed4c3c125da6c GIT binary patch literal 3391 zcmZ`*OLH5?5uVu>79c=^;8WDYuq!WAEJ9KgRg`kMOi6IWii9u(BuaDHT4II}pv3~2 z9Z({?$iZ@zsDonX52VQQNq-gTeascF;Ja;SO<>dN4x!g@CK+> zKiuoIfoDIH{y@D&+rel?Nxvn$q3Q(DTXVuYw^84?_qe%vyWur9?r+_zdD|P!n)k=r zV^}cX*xIOj_iG!C2ivtfDKpy`_+j&4AR>^+wT1G6e!DH?TRPcstx;<>H|u|Bc<;8Z z)i-Y4tKESGm)R^z#a3XPMv(en%y!D_7XE(PuZo{T9Up$#_)V3dP-mU>}?u3VO zwQXQ6pJjRJ6-K}2T$^DXjHF~|cYSam{a%OveqtQ;Oo-6i^INJf4_LwDzVdm$?M0w; z3pJcRNTUsJ!xzm$6O$uA<*`O2c*>}cR(|3rxLu|bwt^uS(t?$5h+Ky5hyCh6cJ_TG zs{6jI?)3whkE)}q)tkHFv+k7}-D~pdZ^FS9?|D1Cd2@9A3O?;YwOJi>2A4Zwr2HVb zygwS;JUHlyYI^!fpDJ8U6|PRqFgQpG6V+4xMN|k8L#SMZ_D5RVz|>mb03s$`qNp;u zX4m5MwLyupVdQi(T~}qhxfe!sO=XW}xWP@8KQ)dz`o{fQl1X=Aj-HNKruOLe!_C@GlIuj; zG+|4y?8zBy_UsvnGntYOD8**}wA&RewI%z{57-h0k+(MtTNr8{j^I*BeqyzB8TwIs zanh{7Fv+G)?uid!7~#&uC>bRxF9FrUCDRAzF#l=0#z4d-qNziUjjke55*boqOwXU}>cLL-ZdJp~D-l1)s=FNp~WCb7BL9k!DUFnpzCk^%o?dP*F9 zu1xGnp9kY5ZnFlt3=&zzNi6hS;!ZJ;SiWpWiSzUs_VNQ1xA4gS^Pa>wsK6I3LPLx^ zwP+6Bv^N&1mT2inK?!)!qZl2S7ot6AV%jCI%tP}CBIKA3N}Rqj^a#8(_()k@`<0~} z&B+{EUH8~{LB~eQ$%4OO{0h7!cVCj1^ri9Ad};03+~n3*?%0gY1~~*ARP4H3gVUO4 zLNx=NdHA51lIS6%D@D5W(85rKptTMGUau`m;8>d*hZvp^wA^}aeM7%4k7-PgAb=4} zaJ`(GImU|_-Mrk24Q@ZpMvtI(p1Ly7k_9a_%bjDmG$et!n5be*U+91zLvbEkqwP`t zkj4a5GO4sOixPZ!Zft#t zeB+Up1Wo4Ue|YAo1k?hKx{Jp~OuZ$%6?dl`2pjVoDoe51U50>yWHq)!2%WJFR#}O4 z0!w#TjV++g!Dq!b&%SmkghdQD{)u3jsaH&v>x9ail-*=54Hxf;@G*lYF6KGe;bJuR z4(&R+n0fum^@^EfA|dxXEs?lVME#*`30VfWecl||UyRFNj7O!Dh5ext>!IHh0Bxry z)RR6>%%KdD8A$(X7{M*LNNm_%9>{grBq_q4gMJu^>4iGq0U9b7qzkj`0+c#kbUfaP zupq+zd@3ms%^F)tCe=p%5?L5!%-pHnVsLfkDZedekdKK@&OwvR`=K8ks7@>LMCk7Z zf=f(rvWSzo2%}j397e3r^Hu8!oGMYd#M$dh;K*wzX`!m}Dz3b)W%0NtN)0^F-A~+s zubz0IKFe#3JyaoVON`2-aLbRx$v;vhqk~;u#=U-r^Jr{sn8cabPGUt%sCZ+uh11r+ z%mTZD4g8P?;L9s|Ib1~nJCeV^EwO}4o%hrKEM16h0XN!$hCu!>AVXNh_^)HF(wvzy zmg%C-D;CVB?1pSsD zM4zsonylm`mX(QbN=RW!NHXdF!Q}RsvLt4@Br|mvCXb3GQ3Etp8{u&vKS#kk*l2|} zkkVQ&oH}xPA2f4>M-tFwmKH(3BIvVt!!qSv;8s3l%Tr{dQ^oX$HqstsKmIq+U}VMV zq7xVB``2Jg4Ouoj{q@?1Le-bLswYGvKFLCyg11Mw+D dCW$a?^V9_#z(@0dk@vhNAcI<_Yq_pF_dgIuZW#ao literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/constants.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/constants.py new file mode 100644 index 000000000..4d96877cc --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/constants.py @@ -0,0 +1,6 @@ +SETTINGS_SERVICE = "datadog_trace_service" +SETTINGS_TRACER = "datadog_tracer" +SETTINGS_TRACE_ENABLED = "datadog_trace_enabled" +SETTINGS_DISTRIBUTED_TRACING = "datadog_distributed_tracing" +SETTINGS_ANALYTICS_ENABLED = "datadog_analytics_enabled" +SETTINGS_ANALYTICS_SAMPLE_RATE = "datadog_analytics_sample_rate" diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/patch.py new file mode 100644 index 000000000..579d17935 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/patch.py @@ -0,0 +1,93 @@ +import os + +import pyramid.config + +from ddtrace import config +from ddtrace.vendor import wrapt + +from ...utils.formats import asbool +from ...utils.formats import get_env +from .constants import SETTINGS_ANALYTICS_ENABLED +from .constants import SETTINGS_ANALYTICS_SAMPLE_RATE +from .constants import SETTINGS_DISTRIBUTED_TRACING +from .constants import SETTINGS_SERVICE +from .trace import DD_TWEEN_NAME +from .trace import trace_pyramid + + +config._add( + "pyramid", + dict( + distributed_tracing=asbool(get_env("pyramid", "distributed_tracing", default=True)), + ), +) + +DD_PATCH = "_datadog_patch" + + +def patch(): + """ + Patch pyramid.config.Configurator + """ + if getattr(pyramid.config, DD_PATCH, False): + return + + setattr(pyramid.config, DD_PATCH, True) + _w = wrapt.wrap_function_wrapper + _w("pyramid.config", "Configurator.__init__", traced_init) + + +def traced_init(wrapped, instance, args, kwargs): + settings = kwargs.pop("settings", {}) + service = config._get_service(default="pyramid") + # DEV: integration-specific analytics flag can be not set but still enabled + # globally for web frameworks + old_analytics_enabled = get_env("pyramid", "analytics_enabled") + analytics_enabled = os.environ.get("DD_TRACE_PYRAMID_ANALYTICS_ENABLED", old_analytics_enabled) + if analytics_enabled is not None: + analytics_enabled = asbool(analytics_enabled) + # TODO: why is analytics sample rate a string or a bool here? + old_analytics_sample_rate = get_env("pyramid", "analytics_sample_rate", default=True) + analytics_sample_rate = os.environ.get("DD_TRACE_PYRAMID_ANALYTICS_SAMPLE_RATE", old_analytics_sample_rate) + trace_settings = { + SETTINGS_SERVICE: service, + SETTINGS_DISTRIBUTED_TRACING: config.pyramid.distributed_tracing, + SETTINGS_ANALYTICS_ENABLED: analytics_enabled, + SETTINGS_ANALYTICS_SAMPLE_RATE: analytics_sample_rate, + } + # Update over top of the defaults + # DEV: If we did `settings.update(trace_settings)` then we would only ever + # have the default values. + trace_settings.update(settings) + # If the tweens are explicitly set with 'pyramid.tweens', we need to + # explicitly set our tween too since `add_tween` will be ignored. + insert_tween_if_needed(trace_settings) + + # The original Configurator.__init__ looks up two levels to find the package + # name if it is not provided. This has to be replicated here since this patched + # call will occur at the same level in the call stack. + if not kwargs.get("package", None): + from pyramid.path import caller_package + + kwargs["package"] = caller_package(level=2) + + kwargs["settings"] = trace_settings + wrapped(*args, **kwargs) + trace_pyramid(instance) + + +def insert_tween_if_needed(settings): + tweens = settings.get("pyramid.tweens") + # If the list is empty, pyramid does not consider the tweens have been + # set explicitly. + # And if our tween is already there, nothing to do + if not tweens or not tweens.strip() or DD_TWEEN_NAME in tweens: + return + # pyramid.tweens.EXCVIEW is the name of built-in exception view provided by + # pyramid. We need our tween to be before it, otherwise unhandled + # exceptions will be caught before they reach our tween. + idx = tweens.find(pyramid.tweens.EXCVIEW) + if idx == -1: + settings["pyramid.tweens"] = tweens + "\n" + DD_TWEEN_NAME + else: + settings["pyramid.tweens"] = tweens[:idx] + DD_TWEEN_NAME + "\n" + tweens[idx:] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/trace.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/trace.py new file mode 100644 index 000000000..ffc81fae1 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pyramid/trace.py @@ -0,0 +1,123 @@ +from pyramid.httpexceptions import HTTPException +import pyramid.renderers +from pyramid.settings import asbool + +# project +import ddtrace +from ddtrace import config +from ddtrace.vendor import wrapt + +from .. import trace_utils +from ...constants import ANALYTICS_SAMPLE_RATE_KEY +from ...constants import SPAN_MEASURED_KEY +from ...ext import SpanTypes +from ...internal.logger import get_logger +from .constants import SETTINGS_ANALYTICS_ENABLED +from .constants import SETTINGS_ANALYTICS_SAMPLE_RATE +from .constants import SETTINGS_DISTRIBUTED_TRACING +from .constants import SETTINGS_SERVICE +from .constants import SETTINGS_TRACER +from .constants import SETTINGS_TRACE_ENABLED + + +log = get_logger(__name__) + +DD_TWEEN_NAME = "ddtrace.contrib.pyramid:trace_tween_factory" +DD_SPAN = "_datadog_span" + + +def trace_pyramid(config): + config.include("ddtrace.contrib.pyramid") + + +def includeme(config): + # Add our tween just before the default exception handler + config.add_tween(DD_TWEEN_NAME, over=pyramid.tweens.EXCVIEW) + # ensure we only patch the renderer once. + if not isinstance(pyramid.renderers.RendererHelper.render, wrapt.ObjectProxy): + wrapt.wrap_function_wrapper("pyramid.renderers", "RendererHelper.render", trace_render) + + +def trace_render(func, instance, args, kwargs): + # If the request is not traced, we do not trace + request = kwargs.get("request", {}) + if not request: + log.debug("No request passed to render, will not be traced") + return func(*args, **kwargs) + span = getattr(request, DD_SPAN, None) + if not span: + log.debug("No span found in request, will not be traced") + return func(*args, **kwargs) + + with span.tracer.trace("pyramid.render", span_type=SpanTypes.TEMPLATE) as span: + return func(*args, **kwargs) + + +def trace_tween_factory(handler, registry): + # configuration + settings = registry.settings + service = settings.get(SETTINGS_SERVICE) or "pyramid" + tracer = settings.get(SETTINGS_TRACER) or ddtrace.tracer + enabled = asbool(settings.get(SETTINGS_TRACE_ENABLED, tracer.enabled)) + distributed_tracing = asbool(settings.get(SETTINGS_DISTRIBUTED_TRACING, True)) + + if enabled: + # make a request tracing function + def trace_tween(request): + trace_utils.activate_distributed_headers( + tracer, int_config=config.pyramid, request_headers=request.headers, override=distributed_tracing + ) + + with tracer.trace("pyramid.request", service=service, resource="404", span_type=SpanTypes.WEB) as span: + span.set_tag(SPAN_MEASURED_KEY) + # Configure trace search sample rate + # DEV: pyramid is special case maintains separate configuration from config api + analytics_enabled = settings.get(SETTINGS_ANALYTICS_ENABLED) + + if (config.analytics_enabled and analytics_enabled is not False) or analytics_enabled is True: + span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, settings.get(SETTINGS_ANALYTICS_SAMPLE_RATE, True)) + + setattr(request, DD_SPAN, span) # used to find the tracer in templates + response = None + status = None + try: + response = handler(request) + except HTTPException as e: + # If the exception is a pyramid HTTPException, + # that's still valuable information that isn't necessarily + # a 500. For instance, HTTPFound is a 302. + # As described in docs, Pyramid exceptions are all valid + # response types + response = e + raise + except BaseException: + status = 500 + raise + finally: + # set request tags + if request.matched_route: + span.resource = "{} {}".format(request.method, request.matched_route.name) + span.set_tag("pyramid.route.name", request.matched_route.name) + # set response tags + if response: + status = response.status_code + response_headers = response.headers + else: + response_headers = None + + trace_utils.set_http_meta( + span, + config.pyramid, + method=request.method, + url=request.path_url, + status_code=status, + query=request.query_string, + request_headers=request.headers, + response_headers=response_headers, + ) + return response + + return trace_tween + + # if timing support is not enabled, return the original handler + return handler diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pytest/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pytest/__init__.py new file mode 100644 index 000000000..c62ffd74f --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pytest/__init__.py @@ -0,0 +1,54 @@ +""" +The pytest integration traces test executions. + +Enabling +~~~~~~~~ + +Enable traced execution of tests using ``pytest`` runner by +running ``pytest --ddtrace`` or by modifying any configuration +file read by pytest (``pytest.ini``, ``setup.cfg``, ...):: + + [pytest] + ddtrace = 1 + +You can enable all integrations by using the ``--ddtrace-patch-all`` option or by adding this to your configuration:: + + [pytest] + ddtrace-patch-all = 1 + +Global Configuration +~~~~~~~~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.pytest["service"] + + The service name reported by default for pytest traces. + + This option can also be set with the integration specific ``DD_PYTEST_SERVICE`` environment + variable, or more generally with the `DD_SERVICE` environment variable. + + Default: Name of the repository being tested, otherwise ``"pytest"`` if the repository name cannot be found. + + +.. py:data:: ddtrace.config.pytest["operation_name"] + + The operation name reported by default for pytest traces. + + This option can also be set with the ``DD_PYTEST_OPERATION_NAME`` environment + variable. + + Default: ``"pytest.test"`` +""" + +from ddtrace import config + +from ...utils.formats import get_env + + +# pytest default settings +config._add( + "pytest", + dict( + _default_service="pytest", + operation_name=get_env("pytest", "operation_name", default="pytest.test"), + ), +) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pytest/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pytest/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7ad3953a6c2e76ee7f14a7545365c4296f0f19c6 GIT binary patch literal 1564 zcmcIkO>fgM7*4Zng=|BJUtsM-r8U_AA*})g+A73>ZQ2Ub3dnV1r!z|J$acDA2mZkh z`~d_<{!*`;_7^xzypFT30|{}#Q9f+HU(fSC@tf7v(1Y>#%V)wCJ@1E|%n!T!9B%y# z9@Oi5v_bu>G4}g@!=no{$QH&yKWKQ&|GG#Q*%I6VU8GC2NpGDm(A($!H)|OldaY&h zKJdJ-cg#>;8m0~6!myDdhD(8rA_>!|T4$#$DeS6_!mumIFymqreyT^k#GDI7GixN% z%3q^Gg9922T%N%IsX_>*(69{c*Zd}Gw<)cXgPF7@Xe=pDOKU_#i4rMNJ}O+9VahXx z6eH9c)-dj0C5pJ4YJI@y_{ts3vt{^b~D|;gKv*LIh$K zFGD2D=K0Y!pMzz9z`I)|K}% zlS7iB?Hg=dwPgMshEW8}bSN>T(?K=H$k7-%`0v&nVgYdgqdg0+#%=i{nU-j1;~8|q%`xC-^GqV0vxKKS0TOq1@cvP+ zd)UK=-GjHUw!1(f6BDka7&BpP_z6+mHe}t3V=NUzBL-3ey36bEHqcds`2aI-SL%*K z(m}fxqg5-EW}#|sq$;86s$cMHFber#SDkQeyS?UsuR(qM54VajfG(u5z*AWW3JL$I z4>AWVRx{W}nQO;vd73~lW%ITojPX5cE+XUlHAiUaP=T5&DEe;3!^xR$4aQ_|aQHK$FCsZ22L zH*19}A9nhyaBgKf0?iTUoS+pttUexAm;;uB(63?hqDnNg;6 ze6|^H4aLdFjmIA!sLh8W-@vCMv9)#fXv20~g5UvPTs37m07#-25x7<~^OL2Jcd34(@ST`QNt0%iaJ literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pytest/__pycache__/constants.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pytest/__pycache__/constants.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f633c4f1d79b7c05e8468b7341efdc12dec80a6b GIT binary patch literal 303 zcmWIL<>g`k0<(D&6GMUYV-N=!FakLaKwK;UBvKfn7*ZIc7*m*{m{OR788lg<*a|93 zQj1G&v4F@bW!JpKq?}ZRlA^@q%)E4k{4@oKyh2)OUUErheqOPjpC)q@r(2MtuWPt} zkarY|x2K;=6o-eaPe8nHu=`4eA|{|sVB%M$enC-YSz<}5epzCXep-G`N@`KDewCrV zWl~;wmVsH8QIVl>UV%Y;MS7m4WtE8m2&5P2hv*k%7U*W?6_+IDPvox)Zi literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pytest/__pycache__/plugin.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pytest/__pycache__/plugin.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..21ede6f7988edb879a7c89bb54af9167bdfe8645 GIT binary patch literal 6227 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 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pytest/constants.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pytest/constants.py new file mode 100644 index 000000000..b82144712 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pytest/constants.py @@ -0,0 +1,4 @@ +FRAMEWORK = "pytest" +KIND = "test" + +HELP_MSG = "Enable tracing of pytest functions." diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pytest/plugin.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pytest/plugin.py new file mode 100644 index 000000000..7b155f816 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/pytest/plugin.py @@ -0,0 +1,204 @@ +from doctest import DocTest +import json +from typing import Dict + +import pytest + +import ddtrace +from ddtrace.constants import AUTO_KEEP +from ddtrace.constants import SPAN_KIND +from ddtrace.contrib.pytest.constants import FRAMEWORK +from ddtrace.contrib.pytest.constants import HELP_MSG +from ddtrace.contrib.pytest.constants import KIND +from ddtrace.contrib.trace_utils import int_service +from ddtrace.ext import SpanTypes +from ddtrace.ext import ci +from ddtrace.ext import test +from ddtrace.internal import compat +from ddtrace.internal.logger import get_logger +from ddtrace.pin import Pin + + +PATCH_ALL_HELP_MSG = "Call ddtrace.patch_all before running tests." +log = get_logger(__name__) + + +def is_enabled(config): + """Check if the ddtrace plugin is enabled.""" + return config.getoption("ddtrace") or config.getini("ddtrace") + + +def _extract_span(item): + """Extract span from `pytest.Item` instance.""" + return getattr(item, "_datadog_span", None) + + +def _store_span(item, span): + """Store span at `pytest.Item` instance.""" + setattr(item, "_datadog_span", span) + + +def _extract_repository_name(repository_url): + # type: (str) -> str + """Extract repository name from repository url.""" + try: + return compat.parse.urlparse(repository_url).path.rstrip(".git").rpartition("/")[-1] + except ValueError: + # In case of parsing error, default to repository url + log.warning("Repository name cannot be parsed from repository_url: %s", repository_url) + return repository_url + + +def pytest_addoption(parser): + """Add ddtrace options.""" + group = parser.getgroup("ddtrace") + + group._addoption( + "--ddtrace", + action="store_true", + dest="ddtrace", + default=False, + help=HELP_MSG, + ) + + group._addoption( + "--ddtrace-patch-all", + action="store_true", + dest="ddtrace-patch-all", + default=False, + help=PATCH_ALL_HELP_MSG, + ) + + parser.addini("ddtrace", HELP_MSG, type="bool") + parser.addini("ddtrace-patch-all", PATCH_ALL_HELP_MSG, type="bool") + + +def pytest_configure(config): + config.addinivalue_line("markers", "dd_tags(**kwargs): add tags to current span") + if is_enabled(config): + ci_tags = ci.tags() + if ci_tags.get(ci.git.REPOSITORY_URL, None) and int_service(None, ddtrace.config.pytest) == "pytest": + repository_name = _extract_repository_name(ci_tags[ci.git.REPOSITORY_URL]) + ddtrace.config.pytest["service"] = repository_name + Pin(tags=ci_tags, _config=ddtrace.config.pytest).onto(config) + + +def pytest_sessionfinish(session, exitstatus): + """Flush open tracer.""" + pin = Pin.get_from(session.config) + if pin is not None: + pin.tracer.shutdown() + + +@pytest.fixture(scope="function") +def ddspan(request): + pin = Pin.get_from(request.config) + if pin: + return _extract_span(request.node) + + +@pytest.fixture(scope="session", autouse=True) +def patch_all(request): + if request.config.getoption("ddtrace-patch-all") or request.config.getini("ddtrace-patch-all"): + ddtrace.patch_all() + + +@pytest.hookimpl(tryfirst=True, hookwrapper=True) +def pytest_runtest_protocol(item, nextitem): + pin = Pin.get_from(item.config) + if pin is None: + yield + return + with pin.tracer.trace( + ddtrace.config.pytest.operation_name, + service=int_service(pin, ddtrace.config.pytest), + resource=item.nodeid, + span_type=SpanTypes.TEST.value, + ) as span: + span.context.dd_origin = ci.CI_APP_TEST_ORIGIN + span.context.sampling_priority = AUTO_KEEP + span.set_tags(pin.tags) + span.set_tag(SPAN_KIND, KIND) + span.set_tag(test.FRAMEWORK, FRAMEWORK) + span.set_tag(test.NAME, item.name) + if hasattr(item, "module"): + span.set_tag(test.SUITE, item.module.__name__) + elif hasattr(item, "dtest") and isinstance(item.dtest, DocTest): + span.set_tag(test.SUITE, item.dtest.globs["__name__"]) + span.set_tag(test.TYPE, SpanTypes.TEST.value) + + span.set_tag(test.FRAMEWORK_VERSION, pytest.__version__) + + # We preemptively set FAIL as a status, because if pytest_runtest_makereport is not called + # (where the actual test status is set), it means there was a pytest error + span.set_tag(test.STATUS, test.Status.FAIL.value) + + # Parameterized test cases will have a `callspec` attribute attached to the pytest Item object. + # Pytest docs: https://docs.pytest.org/en/6.2.x/reference.html#pytest.Function + if getattr(item, "callspec", None): + parameters = {"arguments": {}, "metadata": {}} # type: Dict[str, Dict[str, str]] + for param_name, param_val in item.callspec.params.items(): + try: + parameters["arguments"][param_name] = repr(param_val) + except Exception: + parameters["arguments"][param_name] = "Could not encode" + log.warning("Failed to encode %r", param_name, exc_info=True) + span.set_tag(test.PARAMETERS, json.dumps(parameters)) + + markers = [marker.kwargs for marker in item.iter_markers(name="dd_tags")] + for tags in markers: + span.set_tags(tags) + _store_span(item, span) + + yield + + +def _extract_reason(call): + if call.excinfo is not None: + return call.excinfo.value + + +@pytest.hookimpl(hookwrapper=True) +def pytest_runtest_makereport(item, call): + """Store outcome for tracing.""" + outcome = yield + + span = _extract_span(item) + if span is None: + return + + is_setup_or_teardown = call.when == "setup" or call.when == "teardown" + has_exception = call.excinfo is not None + + if is_setup_or_teardown and not has_exception: + return + + result = outcome.get_result() + xfail = hasattr(result, "wasxfail") or "xfail" in result.keywords + has_skip_keyword = any(x in result.keywords for x in ["skip", "skipif", "skipped"]) + + if result.skipped: + if xfail and not has_skip_keyword: + # XFail tests that fail are recorded skipped by pytest, should be passed instead + span.set_tag(test.RESULT, test.Status.XFAIL.value) + span.set_tag(test.XFAIL_REASON, result.wasxfail) + span.set_tag(test.STATUS, test.Status.PASS.value) + else: + span.set_tag(test.STATUS, test.Status.SKIP.value) + reason = _extract_reason(call) + if reason is not None: + span.set_tag(test.SKIP_REASON, reason) + elif result.passed: + span.set_tag(test.STATUS, test.Status.PASS.value) + if xfail and not has_skip_keyword: + # XPass (strict=False) are recorded passed by pytest + span.set_tag(test.XFAIL_REASON, getattr(result, "wasxfail", "XFail")) + span.set_tag(test.RESULT, test.Status.XPASS.value) + else: + if xfail and not has_skip_keyword: + # XPass (strict=True) are recorded failed by pytest, longrepr contains reason + span.set_tag(test.XFAIL_REASON, result.longrepr) + span.set_tag(test.RESULT, test.Status.XPASS.value) + span.set_tag(test.STATUS, test.Status.FAIL.value) + if call.excinfo: + span.set_exc_info(call.excinfo.type, call.excinfo.value, call.excinfo.tb) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/__init__.py new file mode 100644 index 000000000..8b157b0e4 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/__init__.py @@ -0,0 +1,59 @@ +""" +The redis integration traces redis requests. + + +Enabling +~~~~~~~~ + +The redis integration is enabled automatically when using +:ref:`ddtrace-run` or :ref:`patch_all()`. + +Or use :ref:`patch()` to manually enable the integration:: + + from ddtrace import patch + patch(redis=True) + + +Global Configuration +~~~~~~~~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.redis["service"] + + The service name reported by default for redis traces. + + This option can also be set with the ``DD_REDIS_SERVICE`` environment + variable. + + Default: ``"redis"`` + + +Instance Configuration +~~~~~~~~~~~~~~~~~~~~~~ + +To configure particular redis instances use the :ref:`Pin` API:: + + import redis + from ddtrace import Pin + + client = redis.StrictRedis(host="localhost", port=6379) + + # Override service name for this instance + Pin.override(client, service="my-custom-queue") + + # Traces reported for this client will now have "my-custom-queue" + # as the service name. + client.get("my-key") +""" + +from ...utils.importlib import require_modules + + +required_modules = ["redis", "redis.client"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + from .tracers import get_traced_redis + from .tracers import get_traced_redis_from + + __all__ = ["get_traced_redis", "get_traced_redis_from", "patch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a1804507ffb30eb4f6810ab751c943e041a4a73c GIT binary patch literal 1559 zcma)6&u=3&6rTKQ(xFv*;ckUXB5h{6EEEzo6=++bUf87#KQst=oUxO5@z{gyNk|V| z5NE_cz@2}oS5Escz=dbeBvgx5!cjbLJkQ^I{=WD8x_0kH;ar zd;)(nhlvJBKpV6v8hJBmHUiqB?PW7*u_kN%-1zn5q|Mr9MmzLCbn;Hp0gn#tii5nH zbiLgJyO;F9KBOUiXW2>)zYabOz6frEZvuLB7YvWd->o1BXDfy@qud}aEn8}0xsu3g zk}*?%(Cmkj8Ec|246h}b3oe)8PxT1jf&hTb`7(+~X;lum84+TOHY+AkX&lBxv&AH( z)JGfXQeMEoaw1C~waNuqIPIfz zP9|Z9U@WxCQ5_rcyinRAAIN|8*1(fAo#~Pdfrrn8niGMZE4kpyvSMw|$&MTM48tfw z#dbo8C6mc6NR;`Fkq7;@Z5jP<|s={QO16(mXS^l=#hlk+X2Y(Yw4(E`}1bgQ%i zGTC2m)V6{d*m$xbiyDE7z!QohFS`ilASxzD`QpQgELP2dhv;PD+ zs;B4AJ{p6}t~TC7FV;+JPTvs431A)FyTrXW#Eew!Kd9n9*@-#r=i5F*RX3W4YysO58Tv- z>GNUu(8ZXl4CCaav|N~|l2h=xZh}X0T4zM}DV^lpxGQ6~;E|H9kFU+Y5gan~JH~$m z+FhuA`fC#xny-mvF_d+@P=YdT;`^uZcrG`u&dy)Gub)1X#Tou_DaYgc56;|J7V#`D zcro(&3Nc#W7vt?VXK{T|?4MB7P*L;P(-33KCASzy#rC0hQE__d*fXavwd12!uNgMl JjU)GK{sX^o^6>xw literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..210d66f9b32c9481b546b996a89f8f3aacda3217 GIT binary patch literal 3765 zcmb6cO>Y~=b-%g%5Ji1|*iE-ao0LsV%8BDvt!q@Vl~$qcyf<&(=kVjXxsn9W zy+3~CeqEEKzu{#1F<|mDc+A2v zLen+{ZUSDkivlkOrLb(51zrMNvMT~F2XkT7t_r*o%!f6*2KXGQ1`FY$y(sW`z?bYL zS@M+OGO2khq(&B=%U7t$B3b%EvRBD6S%Gnltdcbt*U38BfN_Itk{d8?lAGjPFy0_? zN2SBKVVMk(Nn+c zb%8#4P^<>;LwM6a17H%9iAOzmkGu-?;&{#2s9On8zRxF&Fu z6()Fbf|p2{nIn3MRPz1Nmf)D1;-2QHPI1iVdsvaEtkf%$+H+-C0WIdPWbiELSYH_Y zz3sB@iN#Jl%a2k<`=J*xk659{PGXXlN)|i^Cw^*?eiHay7Z*wFrO{i=3gY;w6@h6Q zu*DM?wcLPtG;*1L3L;aB#a2J{EH|<~I_!B})~4~Zb1RNuArZpkBWoXB2V02MVbt$3 zF>hGEh|j!JkG^C14D@j$5O(4+*mrK-^1~#i%o0v-i2b*$vlx{+oc)r9ID}Un;xd=S z$4-JD{9)_hg-S6IDS9#o==i~nX%)O@UboM@aeNGxrxz1H@d7{c2Gv~O$-I(l)eU^G z(H}%@1y^WE?!J9KwKMcVHU|WdN*%McC+1JUn{}6a?m-%tz;4JYdFZ_CUzq9vIi?50#Cf8Nw^CSA$9(?WE z#_<9z!!g_t)_P{EJMA{t;)un(JY{A`WyLvrA<;Kr^FLTKq5dfts(I?t=209FkEYGR zo#ysobk^It*L#=Vc`r(~oM*?;_V(cJ7Cy&G^PmZb-o^-bgW&e*AlW`Y54|QKLbb^V zwE+U~uqjk(ib!cB=Uf-qGANLu7LrksHMyb~N=dGaepO|RQ44m=)Cy#o)QWFlGF2T41Ah-#@)P%3a43hZ|Rtpp5kTL{g;prC&+=(x1R_IDQIO2XLX%sw;E}zKa)AXps(s9$DYa z)6Im8caCTrg8w4-Fz^UBL=sT0=i??ast=z$qB>$_Xq@8~NU?wC-Ynvp4$XM4BkBgW zRBGZfTzMpJQZJ4BwCizWf9Der#x?A@+=LP%x-Ty}sEHt!kO&6BJcmLVs28|~6^ff8w<9=D!zRv+r)OeR6F=f|%Jt9t z9zDMnLv#(Ke*ka#CICssbS^1|tVvZ#`%-s)l>A3x|0O!yAGqBb0ZQ+`|fjdCGhC=5R9QvOQ$9GsBbavrwTRjxF) zxZLHkbIr`WbU*M@2A9O-!4XPC(FXhmZ@;FqHSPy-*A3DKfQ&i0TmpYLfK{X>{T?2= zF%C4QDjDeM;DGpQ%{pd^CNehLIIWfcZ{9k=Vjqxi??gUSOgxn)kz)^aJk!8ZpJ*XJR9q+)u{$l#05jnw#dbxoVo` G=Dz{5GnLu^ literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/__pycache__/tracers.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/__pycache__/tracers.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..adf207aa8d046c67fcddbe0b644c22db513f156a GIT binary patch literal 857 zcmZXS&1%~~5P)|jIhOxoo6@@vJyeHSPFn(oQd&|M3MGd)?m-lUtXVq>`eS#a*2cHw z+CyKUcfXRZJ>?a8>dq=I!5)~=?u?lEW_D(sPLn7(`~52r1|fg&@>DE!d9PG_Qw9jo zm?YGrlz;(dY$T>oLH(z}EU*^_G@yA;@2FQhh_s-sBS+AI<9p)O!JZPQ z`=BZihnB6W{{3t;e4FgB3a zYyep(oZ-55Ivfp0j#1VVCPc7gWxZfrL|J+fV(y*bxu*17sW?_fk`0k`LpR2qH6>em zp>p*7C6m77LCC<@6kN@WIA&=BXg4qGg7G2>S!pFq23x{A>W{yLoAanp_-+w!cag=Q zAl&t-JD#WCZ$@V~ulVWfG#~jtmg#uBeltQ@=I)i7NBIdh7sT;NvChY1Yqi{WmS)DCb=?R7s+K7E*dx$>uz%del$Cwed(nRL!4b#!C8C;M%Z V!EMamA*EiJw$U~%vq^gv>OaCGz)t`G literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/__pycache__/util.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/__pycache__/util.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..343c31b531fe3d25d401cafac9d57197f600542e GIT binary patch literal 1419 zcmY*ZQE%He5GE;Fwq-j_lc8NPU?7GSsDUI-+aW`SVQZE+4KOzjVrNMN41!|PjuMMf zDcX(=`>ERx*dG|u$L*od`z?Ci%kmcj4A@a#vyQ|ak9Wu8k@t~Dm5PI)ZT$L+zneqo zPc!+j5tzIHN*cg8auLUz^l?aBf{ko}Y`HX(X+Ia*t_^a|&2!r=@Vr~(1=rz4?)-$@ z5-;&GjAdSFq3Xiqxzmb5!3H|$Cv1=i&d$b6p9{vLo>qQGFeP}9us~|jQ@#!&>D*Y5 zM-``*6dHzH<^e)6(JGL=pgX?B-`;R=npkUVYr$XO-@!0_-;7)U+6GE?flZ!hbb+UE zm`4aC0ulkqx}YFgk5K$Mr-5~hT8JW@yL8kGj7$%ZjZQ&(0>_2p*pt<^@@3MERQPY+ zok&VZz8jenME{*C6xvTxdLAV@rE#QmO8D6gq=NRVwlX*FmfBl~FY9e@ui0wfO%9re z?bOareC3jz@6uxZsv}}^FIDW8%y!=8$XC7OH>4^M<~FItDj51&)P}yQb)!BPDydDL z);7=N=wf~2;+cB-T*m9(RZnhiPQF+-tryqYwK#}Z1DR;Q-(MY0;?40m6gAGXvfPcZ zBi#B~Ela6p>bMq<(NoTfoX#|5qa$6h)w{ zMt^|7@eKFS3~IB3e!QMplc7da>kXb!ji>Yt*5s0^jVa|slb^HdJw!7)#dGUJq$${C zW(V!Z$oR~1Q*xc-)-ToMy^gsnON7+y$tck05W7`xudqEp%nEB99JTgV7=XX} zRThr1Ro1!;Vs;itUyUIT*iq^`TU!le2QZQ;l=%+!C-iahIJw2FT2vGisnr)Ut^Ban zII4TEwobf8eLr2EPutC=*J$p)1pVRnX=(e_&Yg$t$FY!Hl}(8IbN7SB)^>fb+1RNc zrt~5TWYtco!E9>79zA`YTG2qK7N}CI6Y|uKmFNan8KCEE!Gv2j56w3i%Wl;(AwB^^ z@)VZ;Z0@s0U?60?+AWenXi Kc|x}e<$nN>9b{er literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/patch.py new file mode 100644 index 000000000..84badc854 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/patch.py @@ -0,0 +1,123 @@ +import redis + +from ddtrace import config +from ddtrace.vendor import wrapt + +from .. import trace_utils +from ...constants import ANALYTICS_SAMPLE_RATE_KEY +from ...constants import SPAN_MEASURED_KEY +from ...ext import SpanTypes +from ...ext import redis as redisx +from ...pin import Pin +from ...utils.wrappers import unwrap +from .util import _extract_conn_tags +from .util import format_command_args + + +config._add("redis", dict(_default_service="redis")) + + +def patch(): + """Patch the instrumented methods + + This duplicated doesn't look nice. The nicer alternative is to use an ObjectProxy on top + of Redis and StrictRedis. However, it means that any "import redis.Redis" won't be instrumented. + """ + if getattr(redis, "_datadog_patch", False): + return + setattr(redis, "_datadog_patch", True) + + _w = wrapt.wrap_function_wrapper + + if redis.VERSION < (3, 0, 0): + _w("redis", "StrictRedis.execute_command", traced_execute_command) + _w("redis", "StrictRedis.pipeline", traced_pipeline) + _w("redis", "Redis.pipeline", traced_pipeline) + _w("redis.client", "BasePipeline.execute", traced_execute_pipeline) + _w("redis.client", "BasePipeline.immediate_execute_command", traced_execute_command) + else: + _w("redis", "Redis.execute_command", traced_execute_command) + _w("redis", "Redis.pipeline", traced_pipeline) + _w("redis.client", "Pipeline.execute", traced_execute_pipeline) + _w("redis.client", "Pipeline.immediate_execute_command", traced_execute_command) + Pin(service=None, app=redisx.APP).onto(redis.StrictRedis) + + +def unpatch(): + if getattr(redis, "_datadog_patch", False): + setattr(redis, "_datadog_patch", False) + + if redis.VERSION < (3, 0, 0): + unwrap(redis.StrictRedis, "execute_command") + unwrap(redis.StrictRedis, "pipeline") + unwrap(redis.Redis, "pipeline") + unwrap(redis.client.BasePipeline, "execute") + unwrap(redis.client.BasePipeline, "immediate_execute_command") + else: + unwrap(redis.Redis, "execute_command") + unwrap(redis.Redis, "pipeline") + unwrap(redis.client.Pipeline, "execute") + unwrap(redis.client.Pipeline, "immediate_execute_command") + + +# +# tracing functions +# +def traced_execute_command(func, instance, args, kwargs): + pin = Pin.get_from(instance) + if not pin or not pin.enabled(): + return func(*args, **kwargs) + + with pin.tracer.trace( + redisx.CMD, service=trace_utils.ext_service(pin, config.redis, pin), span_type=SpanTypes.REDIS + ) as s: + s.set_tag(SPAN_MEASURED_KEY) + query = format_command_args(args) + s.resource = query + s.set_tag(redisx.RAWCMD, query) + if pin.tags: + s.set_tags(pin.tags) + s.set_tags(_get_tags(instance)) + s.set_metric(redisx.ARGS_LEN, len(args)) + # set analytics sample rate if enabled + s.set_tag(ANALYTICS_SAMPLE_RATE_KEY, config.redis.get_analytics_sample_rate()) + # run the command + return func(*args, **kwargs) + + +def traced_pipeline(func, instance, args, kwargs): + pipeline = func(*args, **kwargs) + pin = Pin.get_from(instance) + if pin: + pin.onto(pipeline) + return pipeline + + +def traced_execute_pipeline(func, instance, args, kwargs): + pin = Pin.get_from(instance) + if not pin or not pin.enabled(): + return func(*args, **kwargs) + + # FIXME[matt] done in the agent. worth it? + cmds = [format_command_args(c) for c, _ in instance.command_stack] + resource = "\n".join(cmds) + tracer = pin.tracer + with tracer.trace( + redisx.CMD, + resource=resource, + service=trace_utils.ext_service(pin, config.redis), + span_type=SpanTypes.REDIS, + ) as s: + s.set_tag(SPAN_MEASURED_KEY) + s.set_tag(redisx.RAWCMD, resource) + s.set_tags(_get_tags(instance)) + s.set_metric(redisx.PIPELINE_LEN, len(instance.command_stack)) + + # set analytics sample rate if enabled + s.set_tag(ANALYTICS_SAMPLE_RATE_KEY, config.redis.get_analytics_sample_rate()) + + return func(*args, **kwargs) + + +def _get_tags(conn): + return _extract_conn_tags(conn.connection_pool.connection_kwargs) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/tracers.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/tracers.py new file mode 100644 index 000000000..de812fd58 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/tracers.py @@ -0,0 +1,20 @@ +from redis import StrictRedis + +from ...utils.deprecation import deprecated + + +DEFAULT_SERVICE = "redis" + + +@deprecated(message="Use patching instead (see the docs).", version="1.0.0") +def get_traced_redis(ddtracer, service=DEFAULT_SERVICE, meta=None): + return _get_traced_redis(ddtracer, StrictRedis, service, meta) + + +@deprecated(message="Use patching instead (see the docs).", version="1.0.0") +def get_traced_redis_from(ddtracer, baseclass, service=DEFAULT_SERVICE, meta=None): + return _get_traced_redis(ddtracer, baseclass, service, meta) + + +def _get_traced_redis(ddtracer, baseclass, service, meta): + return baseclass diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/util.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/util.py new file mode 100644 index 000000000..6796ba4d9 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/redis/util.py @@ -0,0 +1,54 @@ +""" +Some utils used by the dogtrace redis integration +""" +from ...ext import net +from ...ext import redis as redisx +from ...internal.compat import stringify + + +VALUE_PLACEHOLDER = "?" +VALUE_MAX_LEN = 100 +VALUE_TOO_LONG_MARK = "..." +CMD_MAX_LEN = 1000 + + +def _extract_conn_tags(conn_kwargs): + """Transform redis conn info into dogtrace metas""" + try: + return { + net.TARGET_HOST: conn_kwargs["host"], + net.TARGET_PORT: conn_kwargs["port"], + redisx.DB: conn_kwargs["db"] or 0, + } + except Exception: + return {} + + +def format_command_args(args): + """Format a command by removing unwanted values + + Restrict what we keep from the values sent (with a SET, HGET, LPUSH, ...): + - Skip binary content + - Truncate + """ + length = 0 + out = [] + for arg in args: + try: + cmd = stringify(arg) + + if len(cmd) > VALUE_MAX_LEN: + cmd = cmd[:VALUE_MAX_LEN] + VALUE_TOO_LONG_MARK + + if length + len(cmd) > CMD_MAX_LEN: + prefix = cmd[: CMD_MAX_LEN - length] + out.append("%s%s" % (prefix, VALUE_TOO_LONG_MARK)) + break + + out.append(cmd) + length += len(cmd) + except Exception: + out.append(VALUE_PLACEHOLDER) + break + + return " ".join(out) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/rediscluster/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/rediscluster/__init__.py new file mode 100644 index 000000000..78b4da70d --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/rediscluster/__init__.py @@ -0,0 +1,29 @@ +"""Instrument rediscluster to report Redis Cluster queries. + +``patch_all`` will automatically patch your Redis Cluster client to make it work. +:: + + from ddtrace import Pin, patch + import rediscluster + + # If not patched yet, you can patch redis specifically + patch(rediscluster=True) + + # This will report a span with the default settings + client = rediscluster.StrictRedisCluster(startup_nodes=[{'host':'localhost', 'port':'7000'}]) + client.get('my-key') + + # Use a pin to specify metadata related to this client + Pin.override(client, service='redis-queue') +""" + +from ...utils.importlib import require_modules + + +required_modules = ["rediscluster", "rediscluster.client"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + + __all__ = ["patch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/rediscluster/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/rediscluster/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3bdf43eba8a426687d8d5159910265ba2de2e8f8 GIT binary patch literal 1035 zcmZ8gU2oGc6m`;;Zc(Qp?Ue^hJfxM{q~l}Ii6SHr(q1Omhd@Cn^rh=t7x!3l&=N#XhZf!XT#@^2#r!7!#u#+Xv0NCc~K z$>I%9`W0firOvKXuFl@a2UDC#Q|)CG=gjmA5;!7BO{qMEb;=?>t+ILnR~w>C zmWGL>OpiZ&cIQ$Xci$Bf>@2_g*e$$(+>_yO=zjUwTdUyBnCZH4KDc1Hd&~J$GfZ&G zlfvyvagJkV2qlI9B|!{$1Z$=cS8**t!EaA4m{OdwZk6wYFty+j8@r`R1L%c}L9J_O zS=6(xwiUa~xME=}X(pHsI_qYxcGe@W!o0Ssqpi|>)r9D<*S>0pA(c@W2HTn8LVMM9 z3x1}cWl?aqE<|rDZO2?|@cbrGA{8*uiDCE)sWzM|r$1l)RPhDq_7_C?QzOGcyBBd~fnoTdJWpYn9T;eP?l!D5w;@;qjK-KV$jNG5Q% mXa2faKMZ-oO&EGHfcP!@%2WRW;(>FHR%L z$e$=oJ_ZP1!khjM4JVw2gki6RG}4$x5$mBI8O%`F0BkZ-VKcNMo7oCmfF0&2Y=^lh z&+-a8;Z#&$1%-2fr&&?qd{~NR*o?wc;cPU=<`gc3^U(rZpd?rf2%io}XX#jH7kH7E zo)dPF&+u93m-!r@hkk|A7AY^h2HQ!aOf$>x#=Gr3K+Cm8ZS&Fg#(K+b)$TVp>+V)< zyY7Bne*}}Wt!Ay^-mlkMJ6rX;qroL9JU`%Wa1i)C8MuBoioBSEdTBJ1w38rg#{mq@ z?{-Dx$+42_iMmz;i`&w|8HF8bpW&Qz z)QX(Ucc$Pi@cawyWLnZr@sv^$uc;MN6RQgotEb<&x`eCIu5z(|wdqO!$*O!3thVD+ zie41NGT^IGAfI%3niXB{Nso8;+yoWxHMU>ruQfQMz*DOxD=4q|VK0?|=)doWZP=r- zg|1v54pz=ZsDJrwO5BUV4kp z_(T0G!^wAjZzi+%0_jO9#4MZ}zdn=-AhAVE4tKW~`?B4QU6hhQWDAg~8?E)toz`}J z%N=EoFwt)oZgSyV`i_keSpS)&yn4V&ckB0RJDb~XtG@MMW4+GwTC>TFZY;Z5Ve-J) z+*skUf=I0w+|1apvt*Az*!HK3{iMoRfdn!p{mlRk{o8H zLKnb}bOjos6{tZAnnfL2P`?!z!JoJSv;QhER6i>)3Bp8yC8OekV#DI%I+WF>G@wY# zj>U~*;KMV?Jd^B>!!4BJ57caKLNmwT=;eo6oItVI4N-(|-xDQhS?OF#XU;f$nLU~+ zPlcT>KFT=^Qi! z-KjaW2)`NST=5=^`sm8>lLLV1rnu`50Fs>00X?Nh#=sCu1LH(HCF$yb477p%6Hqtu z+H*3X9sMpjrbi~%PmF;H`qsecm`C=&?pPAsJ3v`67dKuQKR-Bf2F^)AP9ygyWOs@K z>l8u&QVCYf$XVR=n2yE@a!$^77ETsVi5eMM@Q`TabMh_OBELdVu>TG&JP423@R5?n z4{{V9v;}Xv1kD~0?DxeZD$#g)OkZk07_DJplr>?4MrNoxLCdttjg0!|{hMD4+o^;* zCb{z!Zi>5GU`_wychqrZE$sSUnBD;}UYXH+S2St zaXh&n#Jnp6-X@AOvMJZy2Xe;i#2wNGvZAVAb^Zy@A+-y|0M1&}<-IVtgCR?iRUCcD Nspnd=s5|q{{6G9zwhsUR literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/rediscluster/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/rediscluster/patch.py new file mode 100644 index 000000000..6e546045a --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/rediscluster/patch.py @@ -0,0 +1,77 @@ +# 3p +import rediscluster + +# project +from ddtrace import config +from ddtrace.constants import ANALYTICS_SAMPLE_RATE_KEY +from ddtrace.constants import SPAN_MEASURED_KEY +from ddtrace.contrib.redis.patch import traced_execute_command +from ddtrace.contrib.redis.patch import traced_pipeline +from ddtrace.contrib.redis.util import format_command_args +from ddtrace.ext import SpanTypes +from ddtrace.ext import redis as redisx +from ddtrace.pin import Pin +from ddtrace.utils.wrappers import unwrap +from ddtrace.vendor import wrapt + + +# DEV: In `2.0.0` `__version__` is a string and `VERSION` is a tuple, +# but in `1.x.x` `__version__` is a tuple annd `VERSION` does not exist +REDISCLUSTER_VERSION = getattr(rediscluster, "VERSION", rediscluster.__version__) + + +def patch(): + """Patch the instrumented methods""" + if getattr(rediscluster, "_datadog_patch", False): + return + setattr(rediscluster, "_datadog_patch", True) + + _w = wrapt.wrap_function_wrapper + if REDISCLUSTER_VERSION >= (2, 0, 0): + _w("rediscluster", "client.RedisCluster.execute_command", traced_execute_command) + _w("rediscluster", "client.RedisCluster.pipeline", traced_pipeline) + _w("rediscluster", "pipeline.ClusterPipeline.execute", traced_execute_pipeline) + Pin(service=redisx.DEFAULT_SERVICE, app=redisx.APP).onto(rediscluster.RedisCluster) + else: + _w("rediscluster", "StrictRedisCluster.execute_command", traced_execute_command) + _w("rediscluster", "StrictRedisCluster.pipeline", traced_pipeline) + _w("rediscluster", "StrictClusterPipeline.execute", traced_execute_pipeline) + Pin(service=redisx.DEFAULT_SERVICE, app=redisx.APP).onto(rediscluster.StrictRedisCluster) + + +def unpatch(): + if getattr(rediscluster, "_datadog_patch", False): + setattr(rediscluster, "_datadog_patch", False) + + if REDISCLUSTER_VERSION >= (2, 0, 0): + unwrap(rediscluster.client.RedisCluster, "execute_command") + unwrap(rediscluster.client.RedisCluster, "pipeline") + unwrap(rediscluster.pipeline.ClusterPipeline, "execute") + else: + unwrap(rediscluster.StrictRedisCluster, "execute_command") + unwrap(rediscluster.StrictRedisCluster, "pipeline") + unwrap(rediscluster.StrictClusterPipeline, "execute") + + +# +# tracing functions +# + + +def traced_execute_pipeline(func, instance, args, kwargs): + pin = Pin.get_from(instance) + if not pin or not pin.enabled(): + return func(*args, **kwargs) + + cmds = [format_command_args(c.args) for c in instance.command_stack] + resource = "\n".join(cmds) + tracer = pin.tracer + with tracer.trace(redisx.CMD, resource=resource, service=pin.service, span_type=SpanTypes.REDIS) as s: + s.set_tag(SPAN_MEASURED_KEY) + s.set_tag(redisx.RAWCMD, resource) + s.set_metric(redisx.PIPELINE_LEN, len(instance.command_stack)) + + # set analytics sample rate if enabled + s.set_tag(ANALYTICS_SAMPLE_RATE_KEY, config.rediscluster.get_analytics_sample_rate()) + + return func(*args, **kwargs) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/__init__.py new file mode 100644 index 000000000..8000bce32 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/__init__.py @@ -0,0 +1,89 @@ +""" +The ``requests`` integration traces all HTTP requests made with the ``requests`` +library. + +The default service name used is `requests` but it can be configured to match +the services that the specific requests are made to. + +Enabling +~~~~~~~~ + +The requests integration is enabled automatically when using +:ref:`ddtrace-run` or :ref:`patch_all()`. + +Or use :ref:`patch()` to manually enable the integration:: + + from ddtrace import patch + patch(requests=True) + + # use requests like usual + + +Global Configuration +~~~~~~~~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.requests['service'] + + The service name reported by default for requests queries. This value will + be overridden by an instance override or if the split_by_domain setting is + enabled. + + This option can also be set with the ``DD_REQUESTS_SERVICE`` environment + variable. + + Default: ``"requests"`` + + +.. py:data:: ddtrace.config.requests['distributed_tracing'] + + Whether or not to parse distributed tracing headers. + + Default: ``True`` + + +.. py:data:: ddtrace.config.requests['trace_query_string'] + + Whether or not to include the query string as a tag. + + Default: ``False`` + + +.. py:data:: ddtrace.config.requests['split_by_domain'] + + Whether or not to use the domain name of requests as the service name. This + setting can be overridden with session overrides (described in the Instance + Configuration section). + + Default: ``False`` + + +Instance Configuration +~~~~~~~~~~~~~~~~~~~~~~ + +To set configuration options for all requests made with a ``requests.Session`` object +use the config API:: + + from ddtrace import config + from requests import Session + + session = Session() + cfg = config.get_from(session) + cfg['service_name'] = 'auth-api' + cfg['distributed_tracing'] = False +""" +from ...utils.importlib import require_modules + + +required_modules = ["requests"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + from .patch import unpatch + from .session import TracedSession + + __all__ = [ + "patch", + "unpatch", + "TracedSession", + ] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..217fbbac453cf5c8dbe8e6392d85cf0693ae5194 GIT binary patch literal 2328 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 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/__pycache__/constants.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/__pycache__/constants.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5b2278e221326f028f9e1081a192c09a0642a547 GIT binary patch literal 242 zcmWIL<>g`k0<(D&6J>$)V-N=!FakLaKwQiLBvKfn7*ZI688n%0aTKK%mZlb$6#Hp1 zM)AA2x;cjWgv1BC28DS#yRKv?Vgf1$6ThnU3yLz!5=&C`%My$9)ADmtQj3c9s|@um zlk&>549v2OiVThO3Jl^a((^1Wt4s_)AiY38M86=jKsPh5xFj(rN4Ko1z_PM3H&s6+ wrKBh^89^uK=am#?Ch5c6qYo4SYRM}p)+?yI#bJ}1pHiBWY6tSiXCP((03^{v`~Uy| literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/__pycache__/legacy.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/__pycache__/legacy.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..afe42b37548d42f9153871fcf07b549f84259658 GIT binary patch literal 1080 zcmcIjOK;Oa5MJA{6G{VAg#(Yn-by5}Q$Ur13POSdTza9s1XZcy9Xqc4Vs_&;QIA|X zzz;xh>|e^2Q~yFFBxdb|7LhAs?d;BaX1;mtLA$-ih<3hx@!vKW`>BPiWRmcTmV87^ zFkvJtHTs6Zgek1VOs&2}=Z3I9v3^51qDi|gTJKrs+67s#jxlqBJPYIK!m7pV0t$q{ zS8<*NhSph-KHIcj(~=K}DT325qVbtPl=0m>V?PXG-(}?9nSXt=a0KExSEE?+6x1je zeB?_$1jzUj2?UQb9{OdDJP+46ks1N7;~HZX3YQb9Aa(gW#ZTfSArBlO&CA+2gA+hr z0!j*7Rxa|e^0JS90O(d%^Ls%OL#A92ONH@ptUwGDDa2Vc-9M190yiHXL!c-?g7*bK zs-tovP=gTj^kMC>^I>a+&#j}mz3q10?zuH>W(rdJ5git>&Nf%rom)uFW~jT{ zZ!MvyR}BeCc+T)9{g|J61;(YXz$<<9!aNa_zBk?Wdc*AGxVv-w1h=1NMR#x-Wxd|? zSyxk3c>7)v7msy9ev&*cr$uivNx>6BSK@M~M0JfkgpXs8N_q)IelT&1$?W!EMbIsZ zNv;!PwrNz~FUQ6Wk^`N`lJxn{7;zH*9-}kka*%eJaA=g8sP)kyHa+|gO*-T+cWDncKJ0{+sZDvTGr2y}c!oL;Sq}#oY({C;cUUzw8 rtl~tv%O^ooT#taRA8L~;z`FZbp|^I^T#OTVsUNS@6Ms5AAmBz z4A|johYr{gdjQ`SU4$p-;Ay)ZH{?naNl=ORLOG zUYuWCsW#NKYF%=zL6VVW)TJH>Q-|qRxQ?juu^-F2W zI^6D8N0ll;VzX3x5Um><@>L7-kwT!^oyy-G^)Wbq-z1f?#V3nQCDT$cu5_|`oScp2 z&GqT?>nG~*Q(2v+KhNaZ+3MLTj#-slB~@NM%B42Y?xV%3I$JIao-l@uzyCrzcuJDD z$|shDQMIfCi?9ZbnmTTa_@o8)D2PZ*)F&|aKUp#9ovdGg^IlfHD=Wy1vVufD^VfJ+ zP^Mt@;JaLBW5KO7oSovf3|Lez5|36pQU7i2{ literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/__pycache__/session.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/__pycache__/session.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d554578091e4d9a28fca84a37c90f2b3345c85c3 GIT binary patch literal 810 zcmY)sJ#W)M^v-r;*L4s=u-}F)!W=-^aW0I7R@c-@b6Zqsi+M^zJ zybruj{hkk0us)z8;19$|h3kk$7`;Iv6wxOnB8gUqw?2*KNXFt&Mz;_pJ?XEIh(F`+ zM>76_Ar@bu$Cj>`Im(TGRjJw7faxtZ4Iwj-sr3@O3j>9Nppw6AFOe&U{-38 zxfBn)xh}8Z04WqRbfkiA(~`_ULz;R;JkS zTBaLr(!5kc8k_D;()qRCu4bpJ7v|)pu4e4xQqSkRS2G98DqW;iQ5_fB)?BIM&90ia z?OLWn^u7M2eGA2PdOxGo?gu%oS`)%gxLdRCq#_&~^qMYkdxEWnY*=s7Mfc$}910jX zO=NsP^y#3QZXm8SQ@8xuxSXBGwp|QawCwM~jGQtYw$xXn0nE7kH;+9xc7D;>evYr* SiP%SgA}_)4d5QldPVgW0Al!fe literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/connection.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/connection.py new file mode 100644 index 000000000..40c0b5065 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/connection.py @@ -0,0 +1,125 @@ +from typing import Optional + +import ddtrace +from ddtrace import config + +from .. import trace_utils +from ...constants import ANALYTICS_SAMPLE_RATE_KEY +from ...constants import SPAN_MEASURED_KEY +from ...ext import SpanTypes +from ...internal.logger import get_logger +from ...propagation.http import HTTPPropagator +from ...utils import get_argument_value + + +log = get_logger(__name__) + + +def _extract_hostname(uri): + # type: (str) -> str + end = len(uri) + j = uri.rfind("#", 0, end) + if j != -1: + end = j + j = uri.rfind("&", 0, end) + if j != -1: + end = j + + start = uri.find("://", 0, end) + 3 + i = uri.find("@", start, end) + 1 + if i != 0: + start = i + j = uri.find("/", start, end) + if j != -1: + end = j + + return uri[start:end] + + +def _extract_query_string(uri): + # type: (str) -> Optional[str] + start = uri.find("?") + 1 + if start == 0: + return None + + end = len(uri) + j = uri.rfind("#", 0, end) + if j != -1: + end = j + + if end <= start: + return None + + return uri[start:end] + + +def _wrap_send(func, instance, args, kwargs): + """Trace the `Session.send` instance method""" + # TODO[manu]: we already offer a way to provide the Global Tracer + # and is ddtrace.tracer; it's used only inside our tests and can + # be easily changed by providing a TracingTestCase that sets common + # tracing functionalities. + tracer = getattr(instance, "datadog_tracer", ddtrace.tracer) + + # skip if tracing is not enabled + if not tracer.enabled: + return func(*args, **kwargs) + + request = get_argument_value(args, kwargs, 0, "request") + if not request: + return func(*args, **kwargs) + + url = request.url + hostname = _extract_hostname(url) + + cfg = config.get_from(instance) + service = None + if cfg["split_by_domain"] and hostname: + service = hostname + if service is None: + service = cfg.get("service", None) + if service is None: + service = cfg.get("service_name", None) + if service is None: + service = trace_utils.ext_service(None, config.requests) + + with tracer.trace("requests.request", service=service, span_type=SpanTypes.HTTP) as span: + span.set_tag(SPAN_MEASURED_KEY) + + # Configure trace search sample rate + # DEV: analytics enabled on per-session basis + cfg = config.get_from(instance) + analytics_enabled = cfg.get("analytics_enabled") + if analytics_enabled: + span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, cfg.get("analytics_sample_rate", True)) + + # propagate distributed tracing headers + if cfg.get("distributed_tracing"): + HTTPPropagator.inject(span.context, request.headers) + + response = response_headers = None + try: + response = func(*args, **kwargs) + return response + finally: + try: + status = None + if response is not None: + status = response.status_code + # Storing response headers in the span. + # Note that response.headers is not a dict, but an iterable + # requests custom structure, that we convert to a dict + response_headers = dict(getattr(response, "headers", {})) + + trace_utils.set_http_meta( + span, + config.requests, + request_headers=request.headers, + response_headers=response_headers, + method=request.method.upper(), + url=request.url, + status_code=status, + query=_extract_query_string(url), + ) + except Exception: + log.debug("requests: error adding tags", exc_info=True) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/constants.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/constants.py new file mode 100644 index 000000000..c3f5eaca5 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/constants.py @@ -0,0 +1 @@ +DEFAULT_SERVICE = "requests" diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/legacy.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/legacy.py new file mode 100644 index 000000000..6a7b4e720 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/legacy.py @@ -0,0 +1,31 @@ +# [Deprecation]: this module contains deprecated functions +# that will be removed in newer versions of the Tracer. +from ddtrace import config + +from ...utils.deprecation import deprecation + + +def _distributed_tracing(self): + """Deprecated: this method has been deprecated in favor of + the configuration system. It will be removed in newer versions + of the Tracer. + """ + deprecation( + name="client.distributed_tracing", + message="Use the configuration object instead `config.get_from(client)['distributed_tracing'`", + version="1.0.0", + ) + return config.get_from(self)["distributed_tracing"] + + +def _distributed_tracing_setter(self, value): + """Deprecated: this method has been deprecated in favor of + the configuration system. It will be removed in newer versions + of the Tracer. + """ + deprecation( + name="client.distributed_tracing", + message="Use the configuration object instead `config.get_from(client)['distributed_tracing'] = value`", + version="1.0.0", + ) + config.get_from(self)["distributed_tracing"] = value diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/patch.py new file mode 100644 index 000000000..298c68fb3 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/patch.py @@ -0,0 +1,49 @@ +import requests + +from ddtrace import config +from ddtrace.vendor.wrapt import wrap_function_wrapper as _w + +from ...pin import Pin +from ...utils.formats import asbool +from ...utils.formats import get_env +from ...utils.wrappers import unwrap as _u +from .connection import _wrap_send +from .legacy import _distributed_tracing +from .legacy import _distributed_tracing_setter + + +# requests default settings +config._add( + "requests", + { + "distributed_tracing": asbool(get_env("requests", "distributed_tracing", default=True)), + "split_by_domain": asbool(get_env("requests", "split_by_domain", default=False)), + "_default_service": "requests", + }, +) + + +def patch(): + """Activate http calls tracing""" + if getattr(requests, "__datadog_patch", False): + return + setattr(requests, "__datadog_patch", True) + + _w("requests", "Session.send", _wrap_send) + Pin(app="requests", _config=config.requests).onto(requests.Session) + + # [Backward compatibility]: `session.distributed_tracing` should point and + # update the `Pin` configuration instead. This block adds a property so that + # old implementations work as expected + fn = property(_distributed_tracing) + fn = fn.setter(_distributed_tracing_setter) + requests.Session.distributed_tracing = fn + + +def unpatch(): + """Disable traced sessions""" + if not getattr(requests, "__datadog_patch", False): + return + setattr(requests, "__datadog_patch", False) + + _u(requests.Session, "send") diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/session.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/session.py new file mode 100644 index 000000000..1e603fb39 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/requests/session.py @@ -0,0 +1,21 @@ +import requests + +from ddtrace import Pin +from ddtrace import config +from ddtrace.vendor.wrapt import wrap_function_wrapper as _w + +from .connection import _wrap_send + + +class TracedSession(requests.Session): + """TracedSession is a requests' Session that is already traced. + You can use it if you want a finer grained control for your + HTTP clients. + """ + + pass + + +# always patch our `TracedSession` when imported +_w(TracedSession, "send", _wrap_send) +Pin(_config=config.requests).onto(TracedSession) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/rq/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/rq/__init__.py new file mode 100644 index 000000000..19ae472c2 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/rq/__init__.py @@ -0,0 +1,251 @@ +""" +The RQ__ integration will trace your jobs. + + +Usage +~~~~~ + +The rq integration is enabled automatically when using +:ref:`ddtrace-run` or :ref:`patch_all()`. + +Or use :ref:`patch()` to manually enable the integration:: + + from ddtrace import patch + patch(rq=True) + + +Worker Usage +~~~~~~~~~~~~ + +``ddtrace-run`` can be used to easily trace your workers:: + + DD_SERVICE=myworker ddtrace-run rq worker + + + +Instance Configuration +~~~~~~~~~~~~~~~~~~~~~~ + +To override the service name for a queue:: + + from ddtrace import Pin + + connection = redis.Redis() + queue = rq.Queue(connection=connection) + Pin.override(queue, service="custom_queue_service") + + +To override the service name for a particular worker:: + + worker = rq.SimpleWorker([queue], connection=queue.connection) + Pin.override(worker, service="custom_worker_service") + + +Global Configuration +~~~~~~~~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.rq['distributed_tracing_enabled'] +.. py:data:: ddtrace.config.rq_worker['distributed_tracing_enabled'] + + If ``True`` the integration will connect the traces sent between the enqueuer + and the RQ worker. + + This option can also be set with the ``DD_RQ_DISTRIBUTED_TRACING_ENABLED`` + environment variable on either the enqueuer or worker applications. + + Default: ``True`` + +.. py:data:: ddtrace.config.rq['service'] + + The service name reported by default for RQ spans from the app. + + This option can also be set with the ``DD_SERVICE`` or ``DD_RQ_SERVICE`` + environment variables. + + Default: ``rq`` + +.. py:data:: ddtrace.config.rq_worker['service'] + + The service name reported by default for RQ spans from workers. + + This option can also be set with the ``DD_SERVICE`` environment + variable. + + Default: ``rq-worker`` + +.. __: https://python-rq.org/ + +""" +import os + +from ddtrace import Pin +from ddtrace import config + +from .. import trace_utils +from ...ext import SpanTypes +from ...propagation.http import HTTPPropagator +from ...utils import get_argument_value +from ...utils.formats import asbool + + +__all__ = [ + "patch", + "unpatch", +] + + +config._add( + "rq", + dict( + distributed_tracing_enabled=asbool(os.environ.get("DD_RQ_DISTRIBUTED_TRACING_ENABLED", True)), + _default_service="rq", + ), +) + +config._add( + "rq_worker", + dict( + distributed_tracing_enabled=asbool(os.environ.get("DD_RQ_DISTRIBUTED_TRACING_ENABLED", True)), + _default_service="rq-worker", + ), +) + + +@trace_utils.with_traced_module +def traced_queue_enqueue_job(rq, pin, func, instance, args, kwargs): + job = get_argument_value(args, kwargs, 0, "f") + + func_name = job.func_name + job_inst = job.instance + job_inst_str = "%s.%s" % (job_inst.__module__, job_inst.__class__.__name__) if job_inst else "" + + if job_inst_str: + resource = "%s.%s" % (job_inst_str, func_name) + else: + resource = func_name + + with pin.tracer.trace( + "rq.queue.enqueue_job", + service=trace_utils.int_service(pin, config.rq), + resource=resource, + span_type=SpanTypes.WORKER, + ) as span: + span._set_str_tag("queue.name", instance.name) + span.set_tag("job.id", job.get_id()) + span._set_str_tag("job.func_name", job.func_name) + + # If the queue is_async then add distributed tracing headers to the job + if instance.is_async and config.rq.distributed_tracing_enabled: + HTTPPropagator.inject(span.context, job.meta) + return func(*args, **kwargs) + + +@trace_utils.with_traced_module +def traced_queue_fetch_job(rq, pin, func, instance, args, kwargs): + with pin.tracer.trace("rq.queue.fetch_job", service=trace_utils.int_service(pin, config.rq)) as span: + job_id = get_argument_value(args, kwargs, 0, "job_id") + span.set_tag("job.id", job_id) + return func(*args, **kwargs) + + +@trace_utils.with_traced_module +def traced_perform_job(rq, pin, func, instance, args, kwargs): + """Trace rq.Worker.perform_job""" + # `perform_job` is executed in a freshly forked, short-lived instance + job = get_argument_value(args, kwargs, 0, "job") + + if config.rq_worker.distributed_tracing_enabled: + ctx = HTTPPropagator.extract(job.meta) + if ctx.trace_id: + pin.tracer.context_provider.activate(ctx) + + try: + with pin.tracer.trace( + "rq.worker.perform_job", + service=trace_utils.int_service(pin, config.rq_worker), + span_type=SpanTypes.WORKER, + resource=job.func_name, + ) as span: + span.set_tag("job.id", job.get_id()) + try: + return func(*args, **kwargs) + finally: + span.set_tag("job.status", job.get_status()) + span.set_tag("job.origin", job.origin) + if job.is_failed: + span.error = 1 + finally: + # Force flush to agent since the process `os.exit()`s + # immediately after this method returns + pin.tracer.writer.flush_queue() + + +@trace_utils.with_traced_module +def traced_job_perform(rq, pin, func, instance, args, kwargs): + """Trace rq.Job.perform(...)""" + job = instance + + # Inherit the service name from whatever parent exists. + # eg. in a worker, a perform_job parent span will exist with the worker + # service. + with pin.tracer.trace("rq.job.perform", resource=job.func_name) as span: + span.set_tag("job.id", job.get_id()) + return func(*args, **kwargs) + + +@trace_utils.with_traced_module +def traced_job_fetch_many(rq, pin, func, instance, args, kwargs): + """Trace rq.Job.fetch_many(...)""" + with pin.tracer.trace("rq.job.fetch_many", service=trace_utils.ext_service(pin, config.rq_worker)) as span: + job_ids = get_argument_value(args, kwargs, 0, "job_ids") + span.set_tag("job_ids", job_ids) + return func(*args, **kwargs) + + +def patch(): + # Avoid importing rq at the module level, eventually will be an import hook + import rq + + if getattr(rq, "_datadog_patch", False): + return + + Pin().onto(rq) + + # Patch rq.job.Job + Pin().onto(rq.job.Job) + trace_utils.wrap(rq.job, "Job.perform", traced_job_perform(rq.job.Job)) + + # Patch rq.queue.Queue + Pin().onto(rq.queue.Queue) + trace_utils.wrap("rq.queue", "Queue.enqueue_job", traced_queue_enqueue_job(rq)) + trace_utils.wrap("rq.queue", "Queue.fetch_job", traced_queue_fetch_job(rq)) + + # Patch rq.worker.Worker + Pin().onto(rq.worker.Worker) + trace_utils.wrap(rq.worker, "Worker.perform_job", traced_perform_job(rq)) + + setattr(rq, "_datadog_patch", True) + + +def unpatch(): + import rq + + if not getattr(rq, "_datadog_patch", False): + return + + Pin().remove_from(rq) + + # Unpatch rq.job.Job + Pin().remove_from(rq.job.Job) + trace_utils.unwrap(rq.job.Job, "perform") + + # Unpatch rq.queue.Queue + Pin().remove_from(rq.queue.Queue) + trace_utils.unwrap(rq.queue.Queue, "enqueue_job") + trace_utils.unwrap(rq.queue.Queue, "fetch_job") + + # Unpatch rq.worker.Worker + Pin().remove_from(rq.worker.Worker) + trace_utils.unwrap(rq.worker.Worker, "perform_job") + + setattr(rq, "_datadog_patch", False) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/rq/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/rq/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..200459e5c0e4e942ad835f9551214077d8fd5a34 GIT binary patch literal 6003 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 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sanic/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sanic/__init__.py new file mode 100644 index 000000000..09bd5fc57 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sanic/__init__.py @@ -0,0 +1,75 @@ +""" +The Sanic__ integration will trace requests to and from Sanic. + + +Enable Sanic tracing automatically via ``ddtrace-run``:: + + ddtrace-run python app.py + +Sanic tracing can also be enabled manually:: + + from ddtrace import patch_all + patch_all(sanic=True) + + from sanic import Sanic + from sanic.response import text + + app = Sanic(__name__) + + @app.route('/') + def index(request): + return text('hello world') + + if __name__ == '__main__': + app.run() + +If using Python 3.6, the legacy ``AsyncioContextProvider`` will have to be +enabled before using the middleware:: + + from ddtrace.contrib.asyncio.provider import AsyncioContextProvider + from ddtrace import tracer # Or whichever tracer instance you plan to use + tracer.configure(context_provider=AsyncioContextProvider()) + + +Configuration +~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.sanic['distributed_tracing_enabled'] + + Whether to parse distributed tracing headers from requests received by your Sanic app. + + Default: ``True`` + + +.. py:data:: ddtrace.config.sanic['service_name'] + + The service name reported for your Sanic app. + + Can also be configured via the ``DD_SERVICE`` environment variable. + + Default: ``'sanic'`` + + +Example:: + + from ddtrace import config + + # Enable distributed tracing + config.sanic['distributed_tracing_enabled'] = True + + # Override service name + config.sanic['service_name'] = 'custom-service-name' + +.. __: https://sanic.readthedocs.io/en/latest/ +""" +from ...utils.importlib import require_modules + + +required_modules = ["sanic"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + from .patch import unpatch + + __all__ = ["patch", "unpatch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sanic/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sanic/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..78f79ad37bfb586710a6d3fdebf0504942ec54e8 GIT binary patch literal 1943 zcma)7!EW0|5T#_vb_l2LCoGTyrJ^RCrZHfl3S8Je zj&|{WkMGagqrG>*%i#OqD)=ejPp*T0$o}pIL3lFFh;R%9upB-XGL zh=C6!6t*B%6Q($s8l6=(aTtb|icMsV;SGeE5ms8AAyCSsT#`ETFG`|*l{rk2a1OVVAVzKTLW-MJ{X}&F1Di!)%qX2I0Tca0(gBg!2 z70lTJTqITz_17GNs58f3MwOrdaBx+J+o;b8D(0UoY0bpcJqtTfPbp<&Xr32C@JmR=v3 z43Qyk=6fXkb#sMb9HWE>oLM#)G{UYO#a>uHM_d$+2NVLQE)uK?tvQW;@wI(72jmlT z(I{PmI@>T=H^Uql)~BdCaowHhNUZW&_0Ka>PpvKFdX-d%KdEZi^N!61 z_zeotVE25)&lb#&Sw)a2Eejl)NA<>$<2CXHqI5v!*5<_^Nqo*YgcwKKxK4{$=mb$2`CHS$4TB$q;Jg0*{T`)= zP3h>cv_hf}KTD;Um^PjmeO`m}HTq#D3O8b!7msib3#Tuoe+6dW`EZwimq~8K0%K0F zyvbBcC+Fnud2%{Y%lF48@4qt7U#R?;-pD|}IZq4%KCdoy9{wB w)Z=0OkL7c1RT^V>@>2TvXRmV0)WlvEr;GQp+VX zJCrPXwK|xlHwSS4XXfddF}LMsr4Jb}ZLY zZ8Nkx1-GEuR_Jt!Zc(-Eu+%BL71b_;W1Xs7Rc$96@6_B0w2QnHp6X1xlW3QDC7kL^ zyVFbyrh}PX=FSGj{%JlI%<(Z^<>Pz$fsS>|Kf`N$;tS0^%TMu1)bo6bPoqA^XZS4Y z^ZYcQLw$jt;b&3T`8+>|`Za!@UqJmjXUkfn{x!Zw(Cu*pFJxm4@!tJRYq1{ZM*w zAhue~z@6l+Sc=waUk2Qxb6U}Qlg=GICK@)%ZIt*rh}04$SzB)#ySkW9Sldi>P%AYe zhqDw)O$}+c3yCJ3HcRxx;QF3PN`iolrPjX~nAWp$EPdIJy=IpOSxp3SuN%dIw-NX} z5OJfJm0EEt!lI}dh%vl3b4H8Y+ATj0?yammcsSga+4*T%VYpuqITZ7)Uot;jw$hlI@VGU4_uosm=7JI&I zZY=b6vU0!2@lKBzn*$qh1w><2W;4eynaRXy^uMJRQVpp)z5yb&Hv6@<#yI2p7tSse zaX4adQ;ir>-!~F=z{J%A^)3(#^{BdGEj8)5(L)-iqbE_~I!HoGjW1bhCOT(xT53t{ zH(Fxt;`$`!vi4kG5>Tx6Jm>YfofUGF^2`~jF*AL!9%uIEwyK#>O2Ce3Y*g|WvuI^S zh4y$SYBtb~9S}_~F^84d;GzP*H-duK^uut~Z*DGJ54&i`Hx>>zzx0FtKqr0&Wb{Gz z0nB^at3FGRI>Z?wXNkN(Tl?l6&kAb3 z)y*bbakDGBxaCnWT7_*SyUZDhLA*}9y+K5AsrXewoqYv2Z(?ms_dv5uR<)@>)VlaC zMovhO_#;Ssg%W=Z@=Xa!ZJ)sq=m#vd5{qk_MR6~&_KgFL>bALW9cZbY*!he^D=`n@ z!??QBfU+ls|OT)-Vb6qL=*oJ_T-{4YOKO)z#CysEMmM#;2u51XA$K#O8g6u zL~Cm)8oYwyQgcv&Fic!o z6FX01X92rb^IcE0&#ykwA|1QfzLS`oCB`4&HveQC=qdaBYC>+u7~K6Jkac|2M%|Bi zT?D-l9w8s-`ND_!3q*a(5AnJ6?G~0r7>nzzwK^dxs0UBs2I7Sm?D`9K1IE{+v$9f$ zER@%Z*1EX~E(K4UL63Y;>{ge_tK1n4WL8TCoj5b#PO{?BaAC+y!^+T9@)lPhGVv}* zW^4vKnWY%Z0ehsQqDL-___$AECS7Z~&2(tw6iN-P3ai2{%rHTI>04kWEh(_NXbi2E zsX%I;0O^NAkfQzuNI&HIJ~Y!VNJxH{9q5Ve6;Hxm`7~wuURcl4`m?lAQZN=|y2M2y zZxMN$$U7jJ4PipAnGL0Ebh+YP`L=FfG=o>b`5J9sAaZ0Yk3=;;gmXVSgmkCHc^JUZ z2K4ngs&(fO)l*E|#CQ|s#6vznq53m4q$ZU+*)>Fs8_Phn>YKX=*AM|1+~C%)^f`^& zo0@M*ix;>fZ8dAgk5#`Y3#xBny~9hgNIg2MQ65})K4S+rjM&CX-mydelrScezKg?mckUJ6H zC}jme&6iT-C@&yvpk7w?ye;^D3c0+jvV3#t{%!BU!#m!U%bD(NXH&V@PrhBrRVaOt z-8hEoBYjzFV}R+n2BMi2`1p!`N>?=#1o&60=0nVqYT9tbWIv@5UmW&z)u%bLZE*x> zn6;ralUut+{6lU-iG@)gia@_n7`*mrq!{`is;Hj)NO$%X!gX=SDU##j7P!2-)NmlF zz7nfzDYb~Zm{9V`3PVK*LY#P?h?3C}QJ$;N&)Mr^!4Sx~oT`8`Vxvzme&X_z?Wg$f zE85V8;YMt)bK?sNUJ$-e81tE#TB)5DQU{k_ZYe3kJXr0*0fTwB+RlCv9o_xv%)aD$$cA% zHaDM^iq+gQ32OBDq+!>1bmx{kM_%2J{BTFMnz0xAogSHPyo7`$J?XCt!iQKPLXkpN zQQzj_vyq$%vY45*x+4Rm5rGFOU+@Lr;rfAF2@q>pr_Je<|7!Gt{8cx}nb1KdR>Y7vjFC&(k A1^@s6 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sanic/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sanic/patch.py new file mode 100644 index 000000000..41f1bfa2b --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sanic/patch.py @@ -0,0 +1,177 @@ +import asyncio + +import sanic + +import ddtrace +from ddtrace import config +from ddtrace.constants import ANALYTICS_SAMPLE_RATE_KEY +from ddtrace.ext import SpanTypes +from ddtrace.pin import Pin +from ddtrace.utils.wrappers import unwrap as _u +from ddtrace.vendor import wrapt +from ddtrace.vendor.wrapt import wrap_function_wrapper as _w + +from .. import trace_utils +from ...internal.logger import get_logger + + +log = get_logger(__name__) + +config._add("sanic", dict(_default_service="sanic", distributed_tracing=True)) + +SANIC_PRE_21 = None + + +def update_span(span, response): + if isinstance(response, sanic.response.BaseHTTPResponse): + status_code = response.status + response_headers = response.headers + else: + # invalid response causes ServerError exception which must be handled + status_code = 500 + response_headers = None + trace_utils.set_http_meta(span, config.sanic, status_code=status_code, response_headers=response_headers) + + +def _wrap_response_callback(span, callback): + # Only for sanic 20 and older + # Wrap response callbacks (either sync or async function) to set HTTP + # response span tags + + @wrapt.function_wrapper + def wrap_sync(wrapped, instance, args, kwargs): + r = wrapped(*args, **kwargs) + response = args[0] + update_span(span, response) + return r + + @wrapt.function_wrapper + async def wrap_async(wrapped, instance, args, kwargs): + r = await wrapped(*args, **kwargs) + response = args[0] + update_span(span, response) + return r + + if asyncio.iscoroutinefunction(callback): + return wrap_async(callback) + + return wrap_sync(callback) + + +async def patch_request_respond(wrapped, instance, args, kwargs): + # Only for sanic 21 and newer + # Wrap the framework response to set HTTP response span tags + response = await wrapped(*args, **kwargs) + pin = Pin._find(instance.ctx) + if pin is not None and pin.enabled(): + span = pin.tracer.current_span() + if span is not None: + update_span(span, response) + return response + + +def _get_path(request): + """Get path and replace path parameter values with names if route exists.""" + path = request.path + try: + match_info = request.match_info + except sanic.exceptions.SanicException: + return path + for key, value in match_info.items(): + try: + value = str(value) + except Exception: + # Best effort + continue + path = path.replace(value, f"<{key}>") + return path + + +async def patch_run_request_middleware(wrapped, instance, args, kwargs): + # Set span resource from the framework request + request = args[0] + pin = Pin._find(request.ctx) + if pin is not None and pin.enabled(): + span = pin.tracer.current_span() + if span is not None: + span.resource = "{} {}".format(request.method, _get_path(request)) + return await wrapped(*args, **kwargs) + + +def patch(): + """Patch the instrumented methods.""" + global SANIC_PRE_21 + + if getattr(sanic, "__datadog_patch", False): + return + setattr(sanic, "__datadog_patch", True) + + SANIC_PRE_21 = sanic.__version__[:2] < "21" + + _w("sanic", "Sanic.handle_request", patch_handle_request) + if not SANIC_PRE_21: + _w("sanic", "Sanic._run_request_middleware", patch_run_request_middleware) + _w(sanic.request, "Request.respond", patch_request_respond) + + +def unpatch(): + """Unpatch the instrumented methods.""" + _u(sanic.Sanic, "handle_request") + if not SANIC_PRE_21: + _u(sanic.Sanic, "_run_request_middleware") + _u(sanic.request.Request, "respond") + if not getattr(sanic, "__datadog_patch", False): + return + setattr(sanic, "__datadog_patch", False) + + +async def patch_handle_request(wrapped, instance, args, kwargs): + """Wrapper for Sanic.handle_request""" + + def unwrap(request, write_callback=None, stream_callback=None, **kwargs): + return request, write_callback, stream_callback, kwargs + + request, write_callback, stream_callback, new_kwargs = unwrap(*args, **kwargs) + + if request.scheme not in ("http", "https"): + return await wrapped(*args, **kwargs) + + pin = Pin() + if SANIC_PRE_21: + # Set span resource from the framework request + resource = "{} {}".format(request.method, _get_path(request)) + else: + # The path is not available anymore in 21.x. Get it from + # the _run_request_middleware instrumented method. + resource = None + pin.onto(request.ctx) + + headers = request.headers.copy() + + trace_utils.activate_distributed_headers(ddtrace.tracer, int_config=config.sanic, request_headers=headers) + + with pin.tracer.trace( + "sanic.request", + service=trace_utils.int_service(None, config.sanic), + resource=resource, + span_type=SpanTypes.WEB, + ) as span: + sample_rate = config.sanic.get_analytics_sample_rate(use_global_config=True) + if sample_rate is not None: + span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, sample_rate) + + method = request.method + url = "{scheme}://{host}{path}".format(scheme=request.scheme, host=request.host, path=request.path) + query_string = request.query_string + if isinstance(query_string, bytes): + query_string = query_string.decode() + trace_utils.set_http_meta( + span, config.sanic, method=method, url=url, query=query_string, request_headers=headers + ) + + if write_callback is not None: + new_kwargs["write_callback"] = _wrap_response_callback(span, write_callback) + if stream_callback is not None: + new_kwargs["stream_callback"] = _wrap_response_callback(span, stream_callback) + + return await wrapped(request, **new_kwargs) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/snowflake/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/snowflake/__init__.py new file mode 100644 index 000000000..a178e7f9a --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/snowflake/__init__.py @@ -0,0 +1,71 @@ +""" +The snowflake integration instruments the ``snowflake-connector-python`` library to trace Snowflake queries. + +Note that this integration is in beta. + +Enabling +~~~~~~~~ + +The integration is not enabled automatically when using +:ref:`ddtrace-run` or :ref:`patch_all()`. + +Use :ref:`patch()` to manually enable the integration:: + + from ddtrace import patch, patch_all + patch(snowflake=True) + patch_all(snowflake=True) + +or the ``DD_TRACE_SNOWFLAKE_ENABLED=true`` to enable it with ``ddtrace-run``. + + +Global Configuration +~~~~~~~~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.snowflake["service"] + + The service name reported by default for snowflake spans. + + This option can also be set with the ``DD_SNOWFLAKE_SERVICE`` environment + variable. + + Default: ``"snowflake"`` + +.. py:data:: ddtrace.config.snowflake["trace_fetch_methods"] + + Whether or not to trace fetch methods. + + Can also configured via the ``DD_SNOWFLAKE_TRACE_FETCH_METHODS`` environment variable. + + Default: ``False`` + + +Instance Configuration +~~~~~~~~~~~~~~~~~~~~~~ + +To configure the integration on an per-connection basis use the +``Pin`` API:: + + from ddtrace import Pin + from snowflake.connector import connect + + # This will report a span with the default settings + conn = connect(user="alice", password="b0b", account="dev") + + # Use a pin to override the service name for this connection. + Pin.override(conn, service="snowflake-dev") + + + cursor = conn.cursor() + cursor.execute("SELECT current_version()") +""" +from ...utils.importlib import require_modules + + +required_modules = ["snowflake.connector"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + from .patch import unpatch + + __all__ = ["patch", "unpatch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/snowflake/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/snowflake/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a13c8fb053b4cb713cb48214ec1c95c7ea288d1b GIT binary patch literal 2012 zcma)7TW{Mo6qavEDx}MR{Q<&zv5VSHcPS7f4wl)<8Z1k@*j)w>gG5WT%}yd!lCtXz z*vo#vus^W({iVF_sefTlJ0vAJDRv`ZikHK4`ObHaUp{>3IxwF8_A7q0>o|Yt&E4?; zHlM*yUcnSOQzxoK)wq&Wr`3uR)fSa$jZ{hP=gKdSrgc)6qo^J=;zrV#HsHPY0Q)D5qqMX56Dv>3G zDTx$F1wkp-2{~gVR9tk@jaqUR1SqC6fyD+Xj+DS5LE}>E>x>9WWY={sxgwwpE10Of zZ%%K~j3^9p9>cSkvW5FoK3wDIJ@+JHTp^+*NrZ5wcmj@xIF2`Hy(A1}Qfuf7GVcdb zWSr`Vj2-7|Se^uk3uGlytiq)auUqZoa(xnjGcP5%D^uLVV~91uEHfspBL=BEZua}G zi(t$JPf%_c(In+UA!FsS{b<8R&>Xf(qz4m`k@oh+IQZ_Z3qjZnjYj_D>U8M&8NfZZ=*KEp8@ayF-n%<`d3MUfKy zb=__krJH_)74G+o0J}gBc-Srd{H`g9SkaI)zc+9g(%BQla6*tE8WBk2Y=a^)$62h< z9HJ^UAemyO83b5PGa4b0x2T77gRARA3VeDOfb3XI{0K!&OaHe^!m94O# znn70e$lC4$D9zG#GYI~7oQ7rooMK*b<=$r+HhJiIGLQto3481Z8o| z1OT(5c$?#GTsGTh-ema9|H_*@dp;W9kMW-aJOkW-5s>b4sC3MrWdA$iI^`3NO2^*i zJA%dm-ls$qJwmsV8J3W;4C)9VaD(6l)g9yX#reOL2M8J7o6;$hp}A#SPfvt1b0Z33edni%AkuZR#dG64fI4f!qAHLXqa%WVNZUE>18+by28^yTB? z;b6{_;2*iDzbAz`LSt2y>Ayx|1Fu>j)x;GG1+tv0y-wg!JN z?aJ+eAg?njh@bE%i;2AX;BP>GTd~3o)2HCoZpO?~)Zs!jV7hGY-R}B+#6#bo9%PEf zvTHk@{)?!B#Pt2#R8-iS9wb!ic3m87)oESO9DVI0Z_-O?+R=Y)ptG~ONsl(0g!J+b vSbhP1CFrbIBGB`F%Bb@FZo0YMJGKy=XfWh^Fx8LuYdcl9Qm^b+_N#vZHd~Zq literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/snowflake/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/snowflake/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e63c9d06a7adaa0b64d5eba2bf2aa75c6d938d30 GIT binary patch literal 1724 zcmZuxOK%%D5GHrIyLzr=%TC<%0Tk#(Xk$5T5x_uDBna&G&^m#wTzFZmL`q&|wYwlG z*%sC(_v!+@^jO%(^e6G!lYc^c2s)%xRcK3sGv5qfGn^T|X*O#J+S6Zuj(_nH`rQcY ztq#I-81gqD3^AOd38oku>;QJCW3ijM6G91K!o1X*_|&)92OQAA;vlU|sYNrN^3*H}GmPC^=D#GTs>*5EA|RbFEa)?^{uxN)yBYuz|^=GINN^&_I2tj)H8 zZ?PTr0C<}{JVl+|d$1REuy$W388Cm4XTxL!$U7Htq3+$qw8Q#lQO1>76w7{|r+|VH zR}s%c`#nWklzDgn4uJ1A%#mcc9_H5YgasC=oBe; zsUg=cMvB~eR`!&Cw=Vk&+3e^9$HH8o!-&NyX89;8Vlelpr-P+aI)KMA-gN@qUOBh7 zROZ6IPOOyB)is^u%IE}v!|9HBvS>KX>=Q*sD!9-a_A1Vz<-T`VG-3HWBt8K4=S8;= z$t+g9JBvkkn5T>j*_}V>9`v*G@&42Cm*UA+S+O5ojIx7+`PciVjf!rsTO`HfB$Fym z)5o)Uad3G#;a$dTI941kRU+wkS1xs}IeW#W_AGh;M#u&bgey*n2@ahW79WEApZFLr zZG4^s_}}8L5M6}?rBe4loYys(bqHXZDAv=Z`3x%mz zv1^EVwFP+uNC(r*`td0U($=;0BXk;Ad<43IvHI^;0bo+kdNBh~qS7hdpYRn?s3Zzs zJ7TvakYmo+Ey-^ie8^f-`l>bF zWZsQ?6_oz%RvFyEA5sR8kN)vIWc@uzkTe;@rlRImkmcZvPvUt4;CU?gSw58aHu%*7;DQkaAhRUkB6 zOf0Kf+JvMY>&Hc613*a>Go}ek2FjL?%|2_+nsYXJZQfR%O>#C(`8VcclAi%-VCayx TtL}n(wzuAs_oP~>S89I&)tI7V literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/snowflake/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/snowflake/patch.py new file mode 100644 index 000000000..b782fa6e7 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/snowflake/patch.py @@ -0,0 +1,62 @@ +from ddtrace import Pin +from ddtrace import config +from ddtrace.vendor import wrapt + +from ...ext import db +from ...ext import net +from ...utils.formats import asbool +from ...utils.formats import get_env +from ...utils.wrappers import unwrap +from ..dbapi import TracedConnection + + +config._add( + "snowflake", + dict( + _default_service="snowflake", + trace_fetch_methods=asbool(get_env("snowflake", "trace_fetch_methods", default=False)), + ), +) + + +def patch(): + import snowflake.connector + + if getattr(snowflake.connector, "_datadog_patch", False): + return + setattr(snowflake.connector, "_datadog_patch", True) + + wrapt.wrap_function_wrapper(snowflake.connector, "Connect", patched_connect) + wrapt.wrap_function_wrapper(snowflake.connector, "connect", patched_connect) + + +def unpatch(): + import snowflake.connector + + if getattr(snowflake.connector, "_datadog_patch", False): + setattr(snowflake.connector, "_datadog_patch", False) + + unwrap(snowflake.connector, "Connect") + unwrap(snowflake.connector, "connect") + + +def patched_connect(connect_func, _, args, kwargs): + conn = connect_func(*args, **kwargs) + if isinstance(conn, TracedConnection): + return conn + + # Add default tags to each query + tags = { + net.TARGET_HOST: conn.host, + net.TARGET_PORT: conn.port, + db.NAME: conn.database, + db.USER: conn.user, + "db.application": conn.application, + "db.schema": conn.schema, + "db.warehouse": conn.warehouse, + } + + pin = Pin(tags=tags) + traced_conn = TracedConnection(conn, pin=pin, cfg=config.snowflake) + pin.onto(traced_conn) + return traced_conn diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlalchemy/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlalchemy/__init__.py new file mode 100644 index 000000000..69b414a75 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlalchemy/__init__.py @@ -0,0 +1,29 @@ +""" +To trace sqlalchemy queries, add instrumentation to the engine class +using the patch method that **must be called before** importing sqlalchemy:: + + # patch before importing `create_engine` + from ddtrace import Pin, patch + patch(sqlalchemy=True) + + # use SQLAlchemy as usual + from sqlalchemy import create_engine + + engine = create_engine('sqlite:///:memory:') + engine.connect().execute("SELECT COUNT(*) FROM users") + + # Use a PIN to specify metadata related to this engine + Pin.override(engine, service='replica-db') +""" +from ...utils.importlib import require_modules + + +required_modules = ["sqlalchemy", "sqlalchemy.event"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .engine import trace_engine + from .patch import patch + from .patch import unpatch + + __all__ = ["trace_engine", "patch", "unpatch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlalchemy/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlalchemy/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..457eaf15490f243f581c3a4395c02b8cc1e94656 GIT binary patch literal 1078 zcmZ8gOK;Oa5MDdanp7>nfK@p(iQ2flDiToys)~eE`b-7##oc%($yVz}ch{kbxN+lx z_ygSeOTBXHU*N>p4o%Bi9*@^E-^_mB_-uLEA-J}G{$Srqg#6B4?ye<#d4snepy9+L z+~T&hLff+~!b`k7vb_@6Q2J_pf9aK>toyvetFjVSy(&gjcum&AnpeyHI{J05j{XvN z_=8c&Tlz>|kYjQ}J`vtHC9P%l$R@<;$J8hm0M(b0$zTlOlwPKw1n3RQIHw}gMx`M{ zh8Yn@)L_CGC`2O>0SzS6+DWyDM)`KaOfaS)m~qT;gBjXxhp9GnfU!(U;OGrw1?@H! zVG=8oWiD#>dX7VJJ*EXx5qu}~JW#+4_(lEmJb0+$kaAv}qd=ucBHAc0@{k;@xu~<# zS1Gjq)k-zc)8qZO^Yb%JQ=Kxo0AD1|TcP*bC~)SK?A-s>T*qW#py#@7FN82w)82Y3 zhb|I2K^#R8m}aX3R}iEInro+f`+K{6x_fwb&~LU|^xetf2kZ%yUb}7j44Y>3=>0(^ ziB2F8!)eAm`4_j|1QLR#GFR?i6xLF;FfFn_kR(U z{c5@S^Tkb+m>^N^gei9zOO6z~vnOtM5PiPb+P-+Io;-_^E&pm1b-T0YTiF^VZr@Er zvYGuFh`BkLCEe*X1b04zw@*~oC^2vs1i8L1B4K>LlT5GcuL?R}XME{ZH06hlQr&i} Jven2g`!CZOM*;u< literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlalchemy/__pycache__/engine.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlalchemy/__pycache__/engine.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..29607b4ee34807e18cf015a4a7337ebf37eaa902 GIT binary patch literal 4468 zcmai1TXWmS6~>JKco8Mpk}s{(qVA+NQ<=2W)~(gV6D85OX&u^;lu3i>3=DFY6etj& z7ocr%pw4vU*L2c9(4ijF-_c)z*FNPhWb)AOEGUYSdjSTEvu7_jd(L+*_ld*6}KYkGU%#XlXM02l3SN_HC!I9xGSKquo_!>t-7nM&X!**?xjZx zTVboO6}HMRAM5TKyTmSozs}a!I^I{9wyQL*`hQUsrLq@0sqnkpNuGy(*xl#DL+AOJ zi-0E^j?b7AL`f>fLms7m8pM&4V!=Llc+?Lf?sP*xNt{?zXqq2kpTl_U_<}p*B#8Q( zo0W=#t0&^2!&vd9U^t3JT6|W#G2HCNQ7VE?b3vfR6<+b>vvqII>k96t{B)w6ITQDN z=iJ!sTUZ>Ve6!VRZ4UV`7KfX+8ghXYb+*OrV)e#M&!AMJX(Tc19CyzT95?QMI1*!~iOtGiFP+TN$zTf3j{Y=1msmUl;fw0Ah- z31;XKPw9n|((7pub!p?%i)e!IM|hGSpvlycohk$MNKLg&&9qE8mH-B1H2)Ssh77s| zWRQwrESh4B&&hBTAw`S;^ZF7pe z`DRkC*;X^CHTMYGVrDnmN1Am(0Ow^8h7RLBe;fjm1-AQP-0{Q3{on!)4E7@E@=Xc< z$RBbi?n!z!AXn4(nsQ~kq3702j@&H1o7aki6=Gq6SsX^xp!nRHZS;*IR?z=$(i(~2 z1(4i&;fq!;4jCZRn%rwW=tM6Eckd70759D-jqZA{`q6_2lV9GYt3PV(wMN0{j&#T{ zyz^o*dT@9+Ykm0$K=3V-CC zi%IWbATiM><%(KY#Z^#U>MmXyIv?OkHqdYd$wqOZW~L#$>r7_`-UcNYyi3etHr^&H zL;V#g^&9ph8_E#yH3+!9;whPq7C~UDXZpZ6AzmrdMmIBMPy%hjs-UjG+MbbGl1m(u zXdE+))6G9rW7zzo)8G^;KHj{>2~ z?TP(%(xkj}*g!({M1@<{LO*b-1~Rm$&i!uv&Z z*`60fLF#!AAWm`(jiQ>k>uOD{>DRQnS~u#dsTt~IebLdHr|Zt4#Kv-hot(yGKpEi*Gjhr!aZ zmi#HLqo)j(GfiS+Ww1KEbfO$tnZ=ZYvY5zqmow``L&Zaw&=6n?`J1<4P%Wc7v zcr3cSu_o=2+kKvTRDDQwNhoM5WNFRNOx2LL_PuFV$nJVY zwbjX6=dFGLuIH>znjr*8>puhW|ExbP!FsTE(j;q5Zf2TjXWGDG>Ol#VIknUBph6t7 z=oiYb6!6RwMOFTIYG3KAjjA-HcpKj=V(8C^k_od0RS77_Bdz3S4`@&JU0cL2WvwM{ z(@d&qTw~|S7rPBh>RC|C6nCg;QbXDmEo$ykb3vb^MusO@LvyOn3Tac*;7M!Rd| zo_Fys#hcTMlH^>8zei_5i5e7iEkm%KyO1_8q!JN+XG*MP`qUy7Np(~Y7}=*FQVsF z$qqV*HHx^BA!?=CK&Kud4{t+k%(x}$&A6ucP;#w-4X#OCWF`gC3#(7mV-+z-oxBHi zqOzAYzx+G$`$z1*N5r8(>Albt=+KK<{Iz|OUgKg^9z9EYw&K?V`4p%pl$I$}Y8 zQgsdRUgJq9wL~bw30s#UEUp>%7l%l+2+A&f1KR|hhdgE9> z0#3ovrv|0|BP8%?Dbs+Ng*+ggtQ|hZB?} z8%w#h?i1%q!Mv)r+Qxfit`W}|MH5vP1J((wdi&?pjHR9Jap8)Ds$k&Eo z1zoWz$028V|V(ziI{a)en4=EVR#AMWMlpuICB&gF{uhi`N%456sZ{U-600000 literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlalchemy/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlalchemy/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..463ffafc8a07937feea8b958663ef5874f227544 GIT binary patch literal 857 zcmZ`%y>8nu5GM6+#deyeeFzP)oU|}tASenH$mk{MqD5dt#gRc#>WOx0ukH)v1+w>* z)UlIap;M2PTPHwEfXDCdq`QwF@AF^~5?qs~NA|-a)fO-NZ%ga!+`rpZJJ<9(*H7z$yh0G$_zI^~zr*})4siKmp z_UasVHl{J_Ev1g^Xy=}qe40VdRG#K?UdTMtm7XsLolm%%OEiioy0AFq*4BpCoLm!S z?<4jWSu2M-+*R%#dw;P0GV*8bahh_b7_a7O%~W=Ob<>8;7r{h!pO@=)@Pf+b>{uA=ec4^0R+gW^}2ZLH(j)~@J=3Pz7rVYVa%^}Ttl(KOU5e(@m(c&4o$o{kEgft zVKJU8F5vu~tjFosxtvZn@5g%0>-Z+Fi~6jPjbcKatv2;^y)N^Za|J9roLMCm6u0q# zZftHhs@JV+P!FjzW+Ld2I&^6FDZIki|J9yg_J!IPf0ahC(RUsM^#_?;qcBZ?28*_D2@1GQc%d+B2k$=!*YE%oXzSXCFI|zRR De*wfY literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlalchemy/engine.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlalchemy/engine.py new file mode 100644 index 000000000..8662864a7 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlalchemy/engine.py @@ -0,0 +1,153 @@ +""" +To trace sqlalchemy queries, add instrumentation to the engine class or +instance you are using:: + + from ddtrace import tracer + from ddtrace.contrib.sqlalchemy import trace_engine + from sqlalchemy import create_engine + + engine = create_engine('sqlite:///:memory:') + trace_engine(engine, tracer, 'my-database') + + engine.connect().execute('select count(*) from users') +""" +# 3p +import sqlalchemy +from sqlalchemy.event import listen + +# project +import ddtrace +from ddtrace import config + +from ...constants import ANALYTICS_SAMPLE_RATE_KEY +from ...constants import SPAN_MEASURED_KEY +from ...ext import SpanTypes +from ...ext import net as netx +from ...ext import sql as sqlx +from ...pin import Pin + + +def trace_engine(engine, tracer=None, service=None): + """ + Add tracing instrumentation to the given sqlalchemy engine or instance. + + :param sqlalchemy.Engine engine: a SQLAlchemy engine class or instance + :param ddtrace.Tracer tracer: a tracer instance. will default to the global + :param str service: the name of the service to trace. + """ + tracer = tracer or ddtrace.tracer # by default use global + EngineTracer(tracer, service, engine) + + +def _wrap_create_engine(func, module, args, kwargs): + """Trace the SQLAlchemy engine, creating an `EngineTracer` + object that will listen to SQLAlchemy events. A PIN object + is attached to the engine instance so that it can be + used later. + """ + # the service name is set to `None` so that the engine + # name is used by default; users can update this setting + # using the PIN object + engine = func(*args, **kwargs) + EngineTracer(ddtrace.tracer, None, engine) + return engine + + +class EngineTracer(object): + def __init__(self, tracer, service, engine): + self.tracer = tracer + self.engine = engine + self.vendor = sqlx.normalize_vendor(engine.name) + self.service = service or self.vendor + self.name = "%s.query" % self.vendor + + # attach the PIN + Pin(app=self.vendor, tracer=tracer, service=self.service).onto(engine) + + listen(engine, "before_cursor_execute", self._before_cur_exec) + listen(engine, "after_cursor_execute", self._after_cur_exec) + + # Determine name of error event to listen for + # Ref: https://github.com/DataDog/dd-trace-py/issues/841 + if sqlalchemy.__version__[0] != "0": + error_event = "handle_error" + else: + error_event = "dbapi_error" + listen(engine, error_event, self._handle_db_error) + + def _before_cur_exec(self, conn, cursor, statement, *args): + pin = Pin.get_from(self.engine) + if not pin or not pin.enabled(): + # don't trace the execution + return + + span = pin.tracer.trace( + self.name, + service=pin.service, + span_type=SpanTypes.SQL, + resource=statement, + ) + span.set_tag(SPAN_MEASURED_KEY) + + if not _set_tags_from_url(span, conn.engine.url): + _set_tags_from_cursor(span, self.vendor, cursor) + + # set analytics sample rate + sample_rate = config.sqlalchemy.get_analytics_sample_rate() + if sample_rate is not None: + span.set_tag(ANALYTICS_SAMPLE_RATE_KEY, sample_rate) + + def _after_cur_exec(self, conn, cursor, statement, *args): + pin = Pin.get_from(self.engine) + if not pin or not pin.enabled(): + # don't trace the execution + return + + span = pin.tracer.current_span() + if not span: + return + + try: + if cursor and cursor.rowcount >= 0: + span.set_tag(sqlx.ROWS, cursor.rowcount) + finally: + span.finish() + + def _handle_db_error(self, *args): + pin = Pin.get_from(self.engine) + if not pin or not pin.enabled(): + # don't trace the execution + return + + span = pin.tracer.current_span() + if not span: + return + + try: + span.set_traceback() + finally: + span.finish() + + +def _set_tags_from_url(span, url): + """set connection tags from the url. return true if successful.""" + if url.host: + span.set_tag(netx.TARGET_HOST, url.host) + if url.port: + span.set_tag(netx.TARGET_PORT, url.port) + if url.database: + span.set_tag(sqlx.DB, url.database) + + return bool(span.get_tag(netx.TARGET_HOST)) + + +def _set_tags_from_cursor(span, vendor, cursor): + """attempt to set db connection tags by introspecting the cursor.""" + if "postgres" == vendor: + if hasattr(cursor, "connection"): + dsn = getattr(cursor.connection, "dsn", None) + if dsn: + d = sqlx.parse_pg_dsn(dsn) + span._set_str_tag(sqlx.DB, d.get("dbname")) + span._set_str_tag(netx.TARGET_HOST, d.get("host")) + span.set_metric(netx.TARGET_PORT, int(d.get("port"))) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlalchemy/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlalchemy/patch.py new file mode 100644 index 000000000..9c364045a --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlalchemy/patch.py @@ -0,0 +1,24 @@ +import sqlalchemy + +from ddtrace.vendor.wrapt import wrap_function_wrapper as _w + +from ...utils.wrappers import unwrap +from .engine import _wrap_create_engine + + +def patch(): + if getattr(sqlalchemy.engine, "__datadog_patch", False): + return + setattr(sqlalchemy.engine, "__datadog_patch", True) + + # patch the engine creation function + _w("sqlalchemy", "create_engine", _wrap_create_engine) + _w("sqlalchemy.engine", "create_engine", _wrap_create_engine) + + +def unpatch(): + # unpatch sqlalchemy + if getattr(sqlalchemy.engine, "__datadog_patch", False): + setattr(sqlalchemy.engine, "__datadog_patch", False) + unwrap(sqlalchemy, "create_engine") + unwrap(sqlalchemy.engine, "create_engine") diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlite3/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlite3/__init__.py new file mode 100644 index 000000000..da4f1c04d --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlite3/__init__.py @@ -0,0 +1,60 @@ +""" +The sqlite integration instruments the built-in sqlite module to trace SQLite queries. + + +Enabling +~~~~~~~~ + +The integration is enabled automatically when using +:ref:`ddtrace-run` or :ref:`patch_all()`. + +Or use :ref:`patch()` to manually enable the integration:: + + from ddtrace import patch + patch(sqlite=True) + + +Global Configuration +~~~~~~~~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.sqlite["service"] + + The service name reported by default for sqlite spans. + + This option can also be set with the ``DD_SQLITE_SERVICE`` environment + variable. + + Default: ``"sqlite"`` + +.. py:data:: ddtrace.config.sqlite["trace_fetch_methods"] + + Whether or not to trace fetch methods. + + Can also configured via the ``DD_SQLITE_TRACE_FETCH_METHODS`` environment variable. + + Default: ``False`` + + +Instance Configuration +~~~~~~~~~~~~~~~~~~~~~~ + +To configure the integration on an per-connection basis use the +``Pin`` API:: + + from ddtrace import Pin + import sqlite3 + + # This will report a span with the default settings + db = sqlite3.connect(":memory:") + + # Use a pin to override the service name for the connection. + Pin.override(db, service='sqlite-users') + + cursor = db.cursor() + cursor.execute("select * from users where id = 1") +""" +from .connection import connection_factory +from .patch import patch + + +__all__ = ["connection_factory", "patch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlite3/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlite3/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..add9318bb90bf265731c09042ce1ab6b0f6f8ef2 GIT binary patch literal 1627 zcma)6-EP}96!uTt3_^ReCpd5~Y8SJVEJKF~NwC6k+W_6#HR=Xr7$i!hZ59-%Qj}8% zL*Jn->M;Ij`^C3p#in{rZ9YGU#+Z+RN?J!^=1L zt4H{tor@0b(w^v+y^CJg%6-u<`xkwbcj#czyV#+3&O75>GC*fRvSd)d72Gi3(y)al zhAWA3ZFEyIX=*U2k~dtK36~q)Qqe{*Fba$&1%vbNpIaAi8>Ts{!ypJ|lH`KRMex2o zfs6G?kQ$iPViZVYREePrBE%XlmrO!aTkBY}d7M$|!c24{k6IbUlMIvwuTc?GEK{@| zjgM~RlMFL?q0tq)t+r7?dn}+NvT;#;F0R~LS;lb?0G_#4CA6`DmzB~6Tp;(=Nh9CL z<3u-Xj4gO7R89m;m7McM<2!rPj!j$aI|#xMs&!0>A#uEk5*E%d^j?1&)=aN>!G=G( zgdA}GACRPEpqb4Hdzr6+vN>slfjRc4MYpbqw7_5~$&%NgDhG5yBoI+6$dL!kz$G_J z*MclNJxvkTvt*W@&tCm-Hl1Y|;GbMU5f zW3#W$|e*o+k?e=8?>^>{iBo(^orRG>7As zMF*CXHo?&_E?KGcIvyg^?R?ya*O)4S3YP>OYA zjgqFnb@Z+^vyVU4QKk8c7#88wL~|u5mJ?m=M~At*e6#oH&6j%rKvsL{yM;VFy!vX- zo<$WUQN=4B)UKkNHzr~<&!ZOdC{4NKCQZX?ef`;y-`6L$(E1KO LcD}ybz1#m2F}@S$ literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlite3/__pycache__/connection.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlite3/__pycache__/connection.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..058ce8b164b3423535a047965e34564762c98a9c GIT binary patch literal 516 zcmYjNy-ve05Vn*4QQ85XAOiy#h*Ln7Dj|f#4g++_g4~=5fy6PkDKw0*GVlWIeWk2S zeFY}Y2t+^W?tFK?^XG3m9QFxf{PE5kjGr!9M+=h^G!vr(0<<6{O(-ScfnRu~pZJsr zA6n3c&YK5a=)IB`nzUf!f`t7QPLU;T`lpqYBDHxXS3Z_PEP+s3q}&Rqccw-Jire%i zmsyZYV+Ds`*9Z~V8xcU2nlP%{qbQEz&}+J-FotKM>CS~VxHEOn{K3NmMLS0`j=v>H zg`R2G!08s(nQ1zAk1j5Wb^?4oGo|x6l3{bM*>zO_Zf5m}O=j}(HXh&Z>(PN!@zql% zCzJXxcE}XFWGYvCjxjHay?L!Bi$y6IfYm(xk7*^X&S%Wr7r7M&4D(+(BDH9?|I@2$ zp4v(;I*z;}Ogue6e-^fr?(ejA3MN|*HZM%{ry{DL+gSg({qv_aw~39WcT`sJP>5qE Mj&TnRyn)}R-+VEJy8r+H literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlite3/__pycache__/patch.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlite3/__pycache__/patch.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e098e911e3632d2ef0be86bbaaf7d81c5010b06c GIT binary patch literal 2625 zcmbsr%WfMtkmNokOS0le96OJuPjuTxavlxfq9_8SO@Sg%TO>t+y)4#C%2{=_Qn-{7 z8}i8pa&mzFKw%&KOMC4pmwrGGL1(xt$#&XPDa>$&ig|FNh_8x@hnPUgLG%I5G|>Up!(b48Cdd zONYc?=1Y7T<`ureS7Bb|m-!mZm%k_c3SU1Yd|gxy41W#y4Sp5)t71(!2c~}oR<7~u zuyS3vaAKX)C&at)26QGJkLGqXNW1Y4P}U0>4%Hh2Hh{bINT}%9QyE5rKOD(yAYrjt z@`E%Lk%|W?tko`@F@B11*$%Vq!5{(3*%2xb={UEu=Si$YzO3-ppo@t7^;su(KyOsSb0li)FFqQjTBTpX3PT}tHz-f!4J|L9@lGaGsFci|LZQ34&9}p0 zdWC2|$6{d?RV%6J?q(H@7g-5n%c$gI70yS=rYzUXb<@7ukoGVHo3@6#xuvP= zXd4?4p*2>cat)|+M#L=!ppCc$gd&Ot3~|p07#i!8c05xq!k)wfvVmwB5h{2FT?RIH zMyY=JBVaOg51}@d>o7-}!xb170GfUF?u1O(#CT2jO+_Z=0mP`h0{dXms=iMf-?MVF z3vSA3kei|0$#VO*7kW0)R~KZFO}qf0l%q=ZN`!$k9jN*Rp$Q$;!D?1&z- z2|b~dOfYMVj>MwjQQrV3d&V^OpZxq7;#xwHA-)M78F);-2aH@nG&d7kNNxaX0hE4m zX_M-;2-eOGlnY1pDIh_7%8Acjk*=2P(De;X8{7oid!leYkt9=7=(n{L zS^yOB-c$gW)+{Y_1S{tT2I~WW4C~)C2y_W&`Px~Db-6CQ-SG^GaWAnd%FhwaV{mSQ zTd?r}Aj2eZsYL#r_oqplq2#moZ5?`y8;2Di0+3tKism8c$eVDub1vcW9xOx#Kz0Yv z#K>BT^k|RCrZQ5d%zZ;y%GQDCU?Ae|#NhPEgf>`IZAdv|7^k@rb$9Y=q}vh%T;{nF zmC)I#JB3z8TeJv>o3M*MjMG>J0o1dP@wm=W%tg70=t79xhPCei%0x9;lkKgZHF;}# z-@nG|kN7E;pb0r9C#Z`~u4!U1=)78f9Wh0W&b>5?(Z_fT>PFb!%DKnO=!Ra_@$8HK zM%jytzd&k!@jL);!p64%Is;tVD7@xv=qm3w%8t@1I-Bp5ok!c*ZA5#DT literal 0 HcmV?d00001 diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlite3/connection.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlite3/connection.py new file mode 100644 index 000000000..3efa5577c --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlite3/connection.py @@ -0,0 +1,8 @@ +from sqlite3 import Connection + +from ...utils.deprecation import deprecated + + +@deprecated(message="Use patching instead (see the docs).", version="1.0.0") +def connection_factory(*args, **kwargs): + return Connection diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlite3/patch.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlite3/patch.py new file mode 100644 index 000000000..41ed07c01 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/sqlite3/patch.py @@ -0,0 +1,78 @@ +# 3p +import sqlite3 +import sqlite3.dbapi2 + +from ddtrace import config +from ddtrace.vendor import wrapt + +# project +from ...contrib.dbapi import FetchTracedCursor +from ...contrib.dbapi import TracedConnection +from ...contrib.dbapi import TracedCursor +from ...pin import Pin +from ...utils.formats import asbool +from ...utils.formats import get_env + + +# Original connect method +_connect = sqlite3.connect + +config._add( + "sqlite", + dict( + _default_service="sqlite", + trace_fetch_methods=asbool(get_env("sqlite", "trace_fetch_methods", default=False)), + ), +) + + +def patch(): + wrapped = wrapt.FunctionWrapper(_connect, traced_connect) + + setattr(sqlite3, "connect", wrapped) + setattr(sqlite3.dbapi2, "connect", wrapped) + + +def unpatch(): + sqlite3.connect = _connect + sqlite3.dbapi2.connect = _connect + + +def traced_connect(func, _, args, kwargs): + conn = func(*args, **kwargs) + return patch_conn(conn) + + +def patch_conn(conn): + wrapped = TracedSQLite(conn) + Pin(app="sqlite").onto(wrapped) + return wrapped + + +class TracedSQLiteCursor(TracedCursor): + def executemany(self, *args, **kwargs): + # DEV: SQLite3 Cursor.execute always returns back the cursor instance + super(TracedSQLiteCursor, self).executemany(*args, **kwargs) + return self + + def execute(self, *args, **kwargs): + # DEV: SQLite3 Cursor.execute always returns back the cursor instance + super(TracedSQLiteCursor, self).execute(*args, **kwargs) + return self + + +class TracedSQLiteFetchCursor(TracedSQLiteCursor, FetchTracedCursor): + pass + + +class TracedSQLite(TracedConnection): + def __init__(self, conn, pin=None, cursor_cls=None): + if not cursor_cls: + # Do not trace `fetch*` methods by default + cursor_cls = TracedSQLiteFetchCursor if config.sqlite.trace_fetch_methods else TracedSQLiteCursor + + super(TracedSQLite, self).__init__(conn, pin=pin, cfg=config.sqlite, cursor_cls=cursor_cls) + + def execute(self, *args, **kwargs): + # sqlite has a few extra sugar functions + return self.cursor().execute(*args, **kwargs) diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/starlette/__init__.py b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/starlette/__init__.py new file mode 100644 index 000000000..d08bab632 --- /dev/null +++ b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/starlette/__init__.py @@ -0,0 +1,86 @@ +""" +The Starlette integration will trace requests to and from Starlette. + + +Enabling +~~~~~~~~ + +The starlette integration is enabled automatically when using +:ref:`ddtrace-run` or :ref:`patch_all()`. + +Or use :ref:`patch()` to manually enable the integration:: + + from ddtrace import patch + from starlette.applications import Starlette + + patch(starlette=True) + app = Starlette() + + +If using Python 3.6, the legacy ``AsyncioContextProvider`` will have to be +enabled before using the middleware:: + + from ddtrace.contrib.asyncio.provider import AsyncioContextProvider + from ddtrace import tracer # Or whichever tracer instance you plan to use + tracer.configure(context_provider=AsyncioContextProvider()) + + +Configuration +~~~~~~~~~~~~~ + +.. py:data:: ddtrace.config.starlette['distributed_tracing'] + + Whether to parse distributed tracing headers from requests received by your Starlette app. + + Can also be enabled with the ``DD_STARLETTE_DISTRIBUTED_TRACING`` environment variable. + + Default: ``True`` + +.. py:data:: ddtrace.config.starlette['analytics_enabled'] + + Whether to analyze spans for starlette in App Analytics. + + Can also be enabled with the ``DD_STARLETTE_ANALYTICS_ENABLED`` environment variable. + + Default: ``None`` + +.. py:data:: ddtrace.config.starlette['service_name'] + + The service name reported for your starlette app. + + Can also be configured via the ``DD_SERVICE`` environment variable. + + Default: ``'starlette'`` + +.. py:data:: ddtrace.config.starlette['request_span_name'] + + The span name for a starlette request. + + Default: ``'starlette.request'`` + + +Example:: + + from ddtrace import config + + # Enable distributed tracing + config.starlette['distributed_tracing'] = True + + # Override service name + config.starlette['service_name'] = 'custom-service-name' + + # Override request span name + config.starlette['request_span_name'] = 'custom-request-span-name' + +""" +from ...utils.importlib import require_modules + + +required_modules = ["starlette"] + +with require_modules(required_modules) as missing_modules: + if not missing_modules: + from .patch import patch + from .patch import unpatch + + __all__ = ["patch", "unpatch"] diff --git a/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/starlette/__pycache__/__init__.cpython-38.pyc b/flask-app/venv/lib/python3.8/site-packages/ddtrace/contrib/starlette/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5bd56e72bc9bdf4e0222b2bf92386b39c9342c5e GIT binary patch literal 2417 zcmb7G-EP}96qX%3KSH|f5eW1m@i5Ejx(0}m1ao6!FczepBfucp)W}C%z>;-nQyFJC;Be%QiE9`2Alq|c8izNc0Fyi6i`F?nqSC1aK4m?M{|3?1WcAUS> zAFIy}{P7k1Bt3XhXW-BhEsIiE9+XQC-58e!8?4MWekuL>*`UHINt;$^RaC?3pbGO9 zx(VaW!6uBi==Qig*m~_ebG~(Y&i4-8xpHbQ`D4p*-2Q~2D@~MOS~J9@W@AM(k0hFL zA&^!iU`VmIDNA&MbcBecXr!WWxyo~0wdZ8c{TMb); zz3KfnLt_1Vnl29er*q6>Lm`Ir9BhGM535&LfcSTEA4$ zlvAdBKhtkQrp$mHGIv4wkc}e6^0WqY$SDuWiv87hGxyUeOn_|)3fbv+No^Cl&lG@B@|IN!)CwEA(M^2BhFYDTfC2AsP8lhyV0 zgaMa;BLa6Q@ZCzN1qY#M!U$-SWS%Mxm0|(oQ#cI6xq+>g7C>MLa5A{?9GD;?F+vt+ zXU6r!dcpVGZG6>l^)5R7eh0U^SN&f1`Kx}Xjr+aUdH3>bumh7*t|A#SsnL`uZq8qp zyUj)<6}ky#GG_Gsd(%NA5py`a3C_)Rmkb+n!%z|vI1=DIRL}`xqZW9+waD^tsIAM^ z#Si`N`4#S5ww_;f+V=%|8OetLO_-YUfMH2OmSZ%HGyjatXK0e9H-L-=q}6eWbghns zk13jRvZ7?C_g(kAb6+U?#Wwp7kS=#0HoDw_1>Z7E1`1hGEMIlUH_IBlJjB-GcCJYn zi?w}a(LOR3vflU@b?m*c?(oiI<7THsFR9JRH%rS2o?*U-MQM?PCOqAWIgzXA7ut#rF*Cb?objA$xYGT13}e_zT% zs`hSy*sWCBp{l@ys>18Cwtc&WF^vL@2YadJBJr}zOz@#9L&V_Id;?nSuov>gT*iwJ z%XB6e+?^Qz=_vT;C42qzt`RFfC7Ly$!8AsZFwL%U^Q3V+l(RR7M{mARPoB#75MPhw z@$t=Y+ WM)C=~D&OpGY?j?prLz?*648yQH+Hu^XqagA~9;v(E{qFR*(`gZCcYpmQ_`@OO zPn;~aCQNqWEq{Yf5J3~-;m8u2GLKP|t;9;X$BoPrJ9RwA$ad05UC%YLlQh$o*D`V= zSxVbp$H;E7oUVAQl*pD`JELA#I_K-6DK|t@w8RoT?U(!lzta)RKM`+JtcX<@uZcC$ zg>g%)iwzjBi%oG2#%)0#k>1u{U@h72(Yo~@79x?Sfs!z@9|Sr)0>KIM>@bc%S~*Zb zC|}WPMN<1o9z{~2>L@!^K>^Yxj{e~&3w4}lKF$iM>Sn0}l}N4Scx^8Bl{_BFQXllV zx9SUd7>p9_mr|X?q4d^fyM9>&nV$uz^wvdOY85{jX(@aSHpN*~uLn`2WE5!WD_Q0v z70NQC=-aG8$Own`Yk14=p%Y|6D+tpz8IlRB*f-?I-HBCM=dgM~e_#_nu`90G(0a<$ z=an_&6H3YP!Ex8D+OXQKs31>W7|ky1Af#;Hv%ue;Q#&dLBg?vxBABvN7JS#^dyntm zuXz#Zqq_0ky@v;|!2blA9;vV(0nu zR1Snd46jCrH=;Y32WSA6hDZJ4dELfvU&6Z$wv-#t5z1+cx^x+O2c9m-3O7QAyR(L_ z&+-2RCYn_Aj82%QLxxdMH!7xX2zG7(s@68C*afSI&jkTY`M$#J8{01?@4T4QPAc_L zE_$~4j>3Bt=3ng`<_hqt-AgNbR?U$*Y6W$+utN~1-8vrW7-prph!10_K7^$bxk8Lb z3cU>R{>x^A;KFNovry*#?V|oD`s=&9( z`Wd8|Z(b{%=bl2+_L$d=Gc6+(m;{>UpfRTB)>e@hwF7~aN-M-zp;1>T7(K|wLI6p6 zcHNlfpQv3E{4A6;4^&jv&Qr*C7+HGyIx0(s6vB_QSYxzG^qJ5$y-t^zdKcsn7Z%%V z(bZG{7H5`PMK1vpoLgrWCE5~P*qWnk5ODVEWgm