Said: add client side
BIN
Client-Side/__pycache__/main.cpython-38.pyc
Normal file
BIN
Client-Side/__pycache__/main.cpython-39.pyc
Normal file
117
Client-Side/main.py
Normal file
|
@ -0,0 +1,117 @@
|
|||
from flask import Flask, render_template, jsonify, Response, request
|
||||
import time
|
||||
import requests
|
||||
import json
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/')
|
||||
def index():
|
||||
return render_template('main.html')
|
||||
|
||||
@app.route("/choose-game", methods=['POST', 'GET'])
|
||||
def choose_game():
|
||||
forward_message = "Moving Forward"
|
||||
# Get game's modes ....
|
||||
# r = requests.get('//API-TO-GAME-SERVER')
|
||||
# json_response = json.dumps(r.json())
|
||||
# rspone = Response(json_response, content_type='application/json; charset=utf-8')
|
||||
# response.headers.add('content-length', len(json_response))
|
||||
# response.status_code = 200
|
||||
# TO-DO : somthings with res ...
|
||||
|
||||
|
||||
return render_template('menu.html', forward_message=forward_message)
|
||||
|
||||
@app.route("/add-images", methods=['POST'])
|
||||
def add_images():
|
||||
return None
|
||||
# TO-DO
|
||||
|
||||
@app.route("/rules", methods=['POST', 'GET'])
|
||||
def rules():
|
||||
return render_template('rules.html')
|
||||
|
||||
|
||||
|
||||
|
||||
@app.route("/new-game", methods=['POST', 'GET'])
|
||||
def new_game():
|
||||
return render_template('new-game.html')
|
||||
|
||||
@app.route("/join-game", methods=['POST', 'GET'])
|
||||
def join_game():
|
||||
return None
|
||||
# TO-DO
|
||||
|
||||
API_URL1 = "http://192.168.37.69:50000"
|
||||
|
||||
#GET PORTS :------------------------------------------------------
|
||||
|
||||
API_URL2 = "http://192.168.37.69:50000/port"
|
||||
|
||||
# @app.route("/test", methods=['GET'])
|
||||
# def test():
|
||||
# res = requests.get(API_URL2)
|
||||
# port = res.json()
|
||||
# return 'Our port is :' + str(port.get('numport')) # our port
|
||||
# # return port
|
||||
|
||||
|
||||
@app.route("/apitest", methods=['POST', 'GET'])
|
||||
def apitest():
|
||||
# reqParms = request
|
||||
# return render_template('test.html', param=reqParms)
|
||||
x = request.get_json(silent=True)
|
||||
print(json.loads(request.data.decode("utf-8")))
|
||||
# log the user out
|
||||
return render_template('test.html', X = x)
|
||||
|
||||
# port = 0
|
||||
|
||||
# def getPort():
|
||||
# res = requests.get(API_URL2)
|
||||
# port = res.json()
|
||||
# return port.get('numport') # our port
|
||||
|
||||
# port = getPort()
|
||||
|
||||
# PORT = requests.get(API_URL2)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# @app.route("/some-url", methods=['GET'])
|
||||
# def get_data():
|
||||
# res = requests.get("http://my-api.com")
|
||||
# return res.content
|
||||
|
||||
|
||||
#TEST Here ...
|
||||
|
||||
@app.route("/getPixels", methods=['GET'])
|
||||
def getPixels():
|
||||
return render_template('test.html')
|
||||
|
||||
# TODO : Req to Discovery service > CardManager
|
||||
# Get All cards > store
|
||||
# Send cards to the view
|
||||
# Manipulate each card
|
||||
# Implement the logic <> IA ):
|
||||
|
||||
|
||||
|
||||
#TO-DO :
|
||||
# WAIT CARDS
|
||||
# SEND UR RESPONSE
|
||||
# SCORE ++ OR --
|
||||
# IF SCORE
|
||||
# Get the timer
|
||||
# Show it
|
||||
#
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run(debug = True)
|
766
Client-Side/static/css/desing.css
Normal file
|
@ -0,0 +1,766 @@
|
|||
body,
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 18px;
|
||||
font-family: "VT323", monospace;
|
||||
line-height: 1.42;
|
||||
background-repeat: no-repeat;
|
||||
background-attachment: fixed;
|
||||
}
|
||||
canvas {
|
||||
display: block;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
min-height: 100vh;
|
||||
position: relative;
|
||||
background-color: #1c2a3a;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
#particles-js {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/*background-image: url("");*/
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
position: absolute;
|
||||
}
|
||||
.logo {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 20px;
|
||||
color: #f0eee4;
|
||||
font-size: 48px;
|
||||
}
|
||||
#high-score {
|
||||
position: absolute;
|
||||
top: 18px;
|
||||
left: 30px;
|
||||
color: #5e99f3;
|
||||
font-size: 40px;
|
||||
}
|
||||
#credits {
|
||||
position: absolute;
|
||||
bottom: 10px;
|
||||
left: 30px;
|
||||
color: #5e99f3;
|
||||
font-size: 15px;
|
||||
}
|
||||
#credits > div {
|
||||
float: left;
|
||||
}
|
||||
.content {
|
||||
width: 60%;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
z-index: 1;
|
||||
flex-direction: column;
|
||||
margin-top: 60px;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.content__container {
|
||||
padding: 40px;
|
||||
border-radius: 10px;
|
||||
max-width: 70%;
|
||||
background-color: #eeece3;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.index__container {
|
||||
width: 70%;
|
||||
}
|
||||
#index__slogan {
|
||||
text-align: center;
|
||||
}
|
||||
.content__btn {
|
||||
padding: 15px 25px;
|
||||
margin-top: 20px;
|
||||
background-color: #5f7ed4;
|
||||
text-transform: uppercase;
|
||||
box-shadow: 0px 6px 0px 0px #5f7ed4, 0px 5px 12px 0px rgba(0, 0, 0, 0.6),
|
||||
inset 0px 0px 10px -5px #1c2a3a;
|
||||
transition: all 100ms linear;
|
||||
cursor: pointer;
|
||||
border-radius: 5px;
|
||||
border: none;
|
||||
height: 60px;
|
||||
color: #fff;
|
||||
}
|
||||
.content__btn:hover {
|
||||
top: 3px;
|
||||
left: -3px;
|
||||
box-shadow: 0px 2px 0px 0px #7690d6, 0px 5px 5px 0px rgba(0, 0, 0, 0.6),
|
||||
inset 0px 0px 10px -5px #1c2a3a;
|
||||
}
|
||||
.content__rules {
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
left: 22px;
|
||||
}
|
||||
#rules__btn {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.extra-info {
|
||||
display: none;
|
||||
color: #fff;
|
||||
line-height: 30px;
|
||||
font-size: 18px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 60px;
|
||||
transform: translate(0, -50%);
|
||||
}
|
||||
|
||||
.info:hover .extra-info {
|
||||
display: block;
|
||||
}
|
||||
.info:hover .icon-info-sign {
|
||||
border: 2px solid transparent;
|
||||
}
|
||||
|
||||
.info {
|
||||
font-size: 20px;
|
||||
padding-left: 5px;
|
||||
width: 20px;
|
||||
border-radius: 15px;
|
||||
}
|
||||
.icon-info-sign {
|
||||
color: #5e99f3;
|
||||
opacity: 0.9;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #4181d4;
|
||||
padding: 10px;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
}
|
||||
.info:hover {
|
||||
background-color: #4dadee;
|
||||
padding: 0 0 0 5px;
|
||||
width: 200px;
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
|
||||
.icon-info-sign:hover{
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.result {
|
||||
width: 80%;
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
#rules__text {
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
color: white;
|
||||
}
|
||||
.explanations {
|
||||
font-family: "Roboto", sans-serif;
|
||||
text-align: left;
|
||||
}
|
||||
.explanations__opaque {
|
||||
color: white;
|
||||
font-family: "VT323", monospace;
|
||||
font-size: 24px;
|
||||
}
|
||||
.explanations__img {
|
||||
box-shadow: 0 3px 6px rgba(0, 0, 0, 0.16), 0 3px 6px rgba(0, 0, 0, 0.23);
|
||||
}
|
||||
|
||||
/* preloader */
|
||||
.preload {
|
||||
animation: rotate 3s linear infinite alternate;
|
||||
position: fixed;
|
||||
display: none;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.brain {
|
||||
width: 200px;
|
||||
}
|
||||
.dots {
|
||||
display: inline-block;
|
||||
animation: opacity 1.5s linear infinite;
|
||||
opacity: 0;
|
||||
}
|
||||
.preload-finish {
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
@keyframes rotate {
|
||||
to {
|
||||
transform: translate(-50%, 50%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes opacity {
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.preload__text {
|
||||
color: #fff;
|
||||
font-size: 28px;
|
||||
}
|
||||
/* game styles */
|
||||
.controls {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
@media screen and (max-width: 1200px) {
|
||||
.controls {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
.controls-btn {
|
||||
padding: 15px 25px;
|
||||
margin-top: 20px;
|
||||
background-color: #abbec3;
|
||||
text-transform: uppercase;
|
||||
box-shadow: 0px 6px 0px 0px #abbec3, 0px 5px 12px 0px rgba(0, 0, 0, 0.6),
|
||||
inset 0px 0px 10px -5px #1c2a3a;
|
||||
transition: all 100ms linear;
|
||||
cursor: pointer;
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
height: 80px;
|
||||
width: 110px;
|
||||
}
|
||||
.controls-btn:hover {
|
||||
top: 3px;
|
||||
left: -3px;
|
||||
box-shadow: 0px 2px 0px 0px #9d6966, 0px 5px 5px 0px rgba(0, 0, 0, 0.6),
|
||||
inset 0px 0px 10px -5px #1c2a3a;
|
||||
}
|
||||
.make-move-form {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
/* coins */
|
||||
.board-list {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.board-list li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
input[type="checkbox"][id^="coin"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.board-list__item label {
|
||||
display: block;
|
||||
position: relative;
|
||||
margin: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.form-wrapper {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
@media screen and (min-width: 1200px) {
|
||||
.board-list__item label {
|
||||
margin: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.board-list__item label:before {
|
||||
background-color: white;
|
||||
color: white;
|
||||
content: " ";
|
||||
display: block;
|
||||
border-radius: 50%;
|
||||
border: 1px solid grey;
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
left: -5px;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
text-align: center;
|
||||
line-height: 28px;
|
||||
transition-duration: 0.4s;
|
||||
transform: scale(0);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.board-list__img {
|
||||
width: 60px;
|
||||
transition-duration: 0.2s;
|
||||
transform-origin: 50% 50%;
|
||||
}
|
||||
|
||||
.board-list__placeholder {
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
:checked + label:before {
|
||||
content: "✓";
|
||||
background-color: grey;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
:checked + label .board-list__img {
|
||||
transform: scale(0.9);
|
||||
z-index: -1;
|
||||
opacity: 0.6;
|
||||
}
|
||||
.row__placeholder {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
border: 1px dashed #fff;
|
||||
background-color: #fff;
|
||||
opacity: 0.2;
|
||||
}
|
||||
.hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
/* alert */
|
||||
.screen-alert {
|
||||
background-color: transparent;
|
||||
border-radius: 2px;
|
||||
border: 1rem solid;
|
||||
border-bottom-color: #3f8df3;
|
||||
border-left-color: #3f8df3;
|
||||
border-right-color: #3f8df3;
|
||||
border-top-color: #3f8df3;
|
||||
pointer-events: none;
|
||||
}
|
||||
.screen-alert #message {
|
||||
background-color: #3f8df3;
|
||||
text-shadow: 0rem 0.2rem 1rem #0c7b46;
|
||||
font-family: "VT323", monospace;
|
||||
font-size: 20px;
|
||||
padding: 10px;
|
||||
color: rgb(255, 255, 255);
|
||||
}
|
||||
.screen-alert #message {
|
||||
background-color: #3f8df3;
|
||||
text-shadow: 0rem 0.2rem 1rem #0c7b46;
|
||||
}
|
||||
.screen-alert__win {
|
||||
color: hotpink;
|
||||
font-size: 50px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.screen-alert__lose {
|
||||
color: red;
|
||||
font-size: 50px;
|
||||
font-weight: bold;
|
||||
}
|
||||
/* animate coins */
|
||||
.bounce-in-top {
|
||||
-webkit-animation: bounce-in-top 1.1s both;
|
||||
animation: bounce-in-top 1.1s both;
|
||||
}
|
||||
.bounce-in-top:nth-child(2) {
|
||||
animation-delay: 0.25s;
|
||||
}
|
||||
.bounce-in-top:nth-child(3) {
|
||||
animation-delay: 0.5s;
|
||||
}
|
||||
.bounce-in-top:nth-child(4) {
|
||||
animation-delay: 0.75s;
|
||||
}
|
||||
.bounce-in-top:nth-child(5) {
|
||||
animation-delay: 1.25s;
|
||||
}
|
||||
.bounce-in-top:nth-child(6) {
|
||||
animation-delay: 1.5s;
|
||||
}
|
||||
.bounce-in-top:nth-child(7) {
|
||||
animation-delay: 1.75s;
|
||||
}
|
||||
.bounce-in-top:nth-child(8) {
|
||||
animation-delay: 2s;
|
||||
}
|
||||
|
||||
/* range styles */
|
||||
.value {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 30px;
|
||||
color: #1c2a3a;
|
||||
text-shadow: white 2px 2px 2px;
|
||||
}
|
||||
.level {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 30px;
|
||||
text-shadow: white 2px 2px 2px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
input[type="range"] {
|
||||
display: block;
|
||||
-webkit-appearance: none;
|
||||
background-color: #bdc3c7;
|
||||
width: 300px;
|
||||
height: 5px;
|
||||
border-radius: 5px;
|
||||
margin: 0 auto;
|
||||
outline: none;
|
||||
padding: 4px 0;
|
||||
}
|
||||
.level2 {
|
||||
color: darkgreen;
|
||||
}
|
||||
.level3 {
|
||||
color: orangered;
|
||||
}
|
||||
.level4 {
|
||||
color: red;
|
||||
}
|
||||
.level5 {
|
||||
color: darkred;
|
||||
}
|
||||
.fade-out {
|
||||
-webkit-animation: fade-out 1s ease-out both;
|
||||
animation: fade-out 1s ease-out both;
|
||||
}
|
||||
/**
|
||||
* ----------------------------------------
|
||||
* animation bounce-in-top
|
||||
* ----------------------------------------
|
||||
*/
|
||||
@-webkit-keyframes bounce-in-top {
|
||||
0% {
|
||||
-webkit-transform: translateY(-500px);
|
||||
transform: translateY(-500px);
|
||||
-webkit-animation-timing-function: ease-in;
|
||||
animation-timing-function: ease-in;
|
||||
opacity: 0;
|
||||
}
|
||||
38% {
|
||||
-webkit-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
-webkit-animation-timing-function: ease-out;
|
||||
animation-timing-function: ease-out;
|
||||
opacity: 1;
|
||||
}
|
||||
55% {
|
||||
-webkit-transform: translateY(-65px);
|
||||
transform: translateY(-65px);
|
||||
-webkit-animation-timing-function: ease-in;
|
||||
animation-timing-function: ease-in;
|
||||
}
|
||||
72% {
|
||||
-webkit-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
-webkit-animation-timing-function: ease-out;
|
||||
animation-timing-function: ease-out;
|
||||
}
|
||||
81% {
|
||||
-webkit-transform: translateY(-28px);
|
||||
transform: translateY(-28px);
|
||||
-webkit-animation-timing-function: ease-in;
|
||||
animation-timing-function: ease-in;
|
||||
}
|
||||
90% {
|
||||
-webkit-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
-webkit-animation-timing-function: ease-out;
|
||||
animation-timing-function: ease-out;
|
||||
}
|
||||
95% {
|
||||
-webkit-transform: translateY(-8px);
|
||||
transform: translateY(-8px);
|
||||
-webkit-animation-timing-function: ease-in;
|
||||
animation-timing-function: ease-in;
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
-webkit-animation-timing-function: ease-out;
|
||||
animation-timing-function: ease-out;
|
||||
}
|
||||
}
|
||||
@keyframes bounce-in-top {
|
||||
0% {
|
||||
-webkit-transform: translateY(-500px);
|
||||
transform: translateY(-500px);
|
||||
-webkit-animation-timing-function: ease-in;
|
||||
animation-timing-function: ease-in;
|
||||
opacity: 0;
|
||||
}
|
||||
38% {
|
||||
-webkit-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
-webkit-animation-timing-function: ease-out;
|
||||
animation-timing-function: ease-out;
|
||||
opacity: 1;
|
||||
}
|
||||
55% {
|
||||
-webkit-transform: translateY(-65px);
|
||||
transform: translateY(-65px);
|
||||
-webkit-animation-timing-function: ease-in;
|
||||
animation-timing-function: ease-in;
|
||||
}
|
||||
72% {
|
||||
-webkit-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
-webkit-animation-timing-function: ease-out;
|
||||
animation-timing-function: ease-out;
|
||||
}
|
||||
81% {
|
||||
-webkit-transform: translateY(-28px);
|
||||
transform: translateY(-28px);
|
||||
-webkit-animation-timing-function: ease-in;
|
||||
animation-timing-function: ease-in;
|
||||
}
|
||||
90% {
|
||||
-webkit-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
-webkit-animation-timing-function: ease-out;
|
||||
animation-timing-function: ease-out;
|
||||
}
|
||||
95% {
|
||||
-webkit-transform: translateY(-8px);
|
||||
transform: translateY(-8px);
|
||||
-webkit-animation-timing-function: ease-in;
|
||||
animation-timing-function: ease-in;
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: translateY(0);
|
||||
transform: translateY(0);
|
||||
-webkit-animation-timing-function: ease-out;
|
||||
animation-timing-function: ease-out;
|
||||
}
|
||||
}
|
||||
|
||||
.ai-ops {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.outcome-alert {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
#board {
|
||||
min-height: 250px;
|
||||
}
|
||||
|
||||
#thinking {
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
#submit-btn {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#set-amount {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
|
||||
/* For the available modes ...*/
|
||||
/*----------------Added just for test --------------*/
|
||||
/*Pas important, juste pour positioner la liste déroulante en milieu de page */
|
||||
.bloc {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items : center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.imgGame {
|
||||
width: 350px;
|
||||
height: 45px;
|
||||
margin-bottom: 0;
|
||||
|
||||
}
|
||||
|
||||
/*Réinitilaisation de la liste déroulante*/
|
||||
select {
|
||||
appearance: none;
|
||||
outline: 0;
|
||||
border: 0 !important;
|
||||
background: #F9F9F9;
|
||||
background-image: none;
|
||||
box-shadow: none;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
/*Ajout des couleurs de fond pour chaque option */
|
||||
select option[value="1"] {
|
||||
background: #2E6DB4;
|
||||
color: white;
|
||||
}
|
||||
|
||||
select option[value="2"] {
|
||||
background: #107C11;
|
||||
color: white;
|
||||
}
|
||||
|
||||
select option[value="3"] {
|
||||
background: #E70009;
|
||||
color: white;
|
||||
}
|
||||
|
||||
select option[value="4"] {
|
||||
background: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Mise en forme de la div .select*/
|
||||
.select {
|
||||
position: relative;
|
||||
width: 350px;
|
||||
height: 45px;
|
||||
overflow: hidden;
|
||||
border-radius: 5px;
|
||||
border: 1px solid #CED4DA;
|
||||
}
|
||||
|
||||
/* Mise en forme de la balise select*/
|
||||
select {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding-left: 15px;
|
||||
color: #555555;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
/* Mise en place de la flèche */
|
||||
.select::after {
|
||||
content: '\276F';
|
||||
position: absolute;
|
||||
top: 20%;
|
||||
right: 0;
|
||||
padding: 0 15px;
|
||||
background: white;
|
||||
pointer-events: none;
|
||||
transform: rotate(90deg);
|
||||
font-size: 1.5em;
|
||||
background: #F9F9F9;
|
||||
}
|
||||
|
||||
|
||||
.select:hover::after {
|
||||
color: #FA6141;
|
||||
}
|
||||
|
||||
/* ====================================== TEST =============================================*/
|
||||
|
||||
/* .text-box { */
|
||||
/* margin-left: 0vw; */
|
||||
/* margin-top: 60px;
|
||||
} */
|
||||
|
||||
.btn1, .btn2 {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* .text-box2{
|
||||
margin-top: 0px;
|
||||
margin-left: -9px;
|
||||
border: red 1px solid;
|
||||
} */
|
||||
|
||||
|
||||
.btn:link,
|
||||
.btn:visited {
|
||||
text-transform: uppercase;
|
||||
text-decoration: none;
|
||||
padding: 15px 40px;
|
||||
display: inline-block;
|
||||
border-radius: 100px;
|
||||
transition: all .2s;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.btn:active {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.btn-white {
|
||||
background-color: #fff;
|
||||
color: #777;
|
||||
}
|
||||
|
||||
.btn::after {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
border-radius: 100px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: -1;
|
||||
transition: all .4s;
|
||||
}
|
||||
|
||||
.btn2 {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.btn1 {
|
||||
float: right
|
||||
}
|
||||
|
||||
.btn-white::after {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.btn:hover::after {
|
||||
transform: scaleX(1.4) scaleY(1.6);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.btn-animated {
|
||||
animation: moveInBottom 5s ease-out;
|
||||
animation-fill-mode: backwards;
|
||||
}
|
||||
|
||||
@keyframes moveInBottom {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(0px);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ====================================== NEW GAME =============================================*/
|
||||
|
143
Client-Side/static/css/new-g.css
Normal file
|
@ -0,0 +1,143 @@
|
|||
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: inherit;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
/* overflow: hidden; */
|
||||
}
|
||||
|
||||
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
/*
|
||||
* Styles for the deck of cards
|
||||
*/
|
||||
|
||||
.deck1 {
|
||||
width: 660px;
|
||||
min-height: 320px;
|
||||
background: linear-gradient(160deg, rgba(46, 61, 73, 0.5) 0%, rgba(224, 229, 233, 0.5) 100%);
|
||||
padding: 32px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 12px 15px 20px 0 rgba(46, 61, 73, 0.5);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: 0 0 2px;
|
||||
}
|
||||
|
||||
.deck2 {
|
||||
width: 660px;
|
||||
min-height: 320px;
|
||||
background: linear-gradient(160deg, rgba(46, 61, 73, 0.5) 0%, rgba(224, 229, 233, 0.5) 100%);
|
||||
padding: 32px;
|
||||
border-radius: 10px;
|
||||
box-shadow: 12px 15px 20px 0 rgba(46, 61, 73, 0.5);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: 10px 0 10px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Styles for the Score Panel
|
||||
*/
|
||||
|
||||
.score-panel {
|
||||
text-align: left;
|
||||
width: 345px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.score-panel .stars {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: inline-block;
|
||||
margin: 0 5px 0 0;
|
||||
}
|
||||
|
||||
.score-panel .stars li {
|
||||
list-style: none;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.gold-star{color: gold}
|
||||
|
||||
.score-panel .restart {
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Popup */
|
||||
.popup {
|
||||
background: #02ccba;
|
||||
z-index: 1;
|
||||
display: none;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 10px;
|
||||
margin: 0 auto;
|
||||
background-color: rgba(255, 255, 255, 0.562);
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
|
||||
}
|
||||
|
||||
.close {
|
||||
color: #000;
|
||||
float: right;
|
||||
font-size: 35px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.close:hover,
|
||||
.close:focus {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.gcard{
|
||||
height: 300px;
|
||||
width: 550px;
|
||||
/* border-radius: 8px; */
|
||||
cursor: pointer;
|
||||
box-shadow: 12px 15px 20px 0 rgba(46, 61, 73, 0.5);
|
||||
}
|
||||
|
BIN
Client-Side/static/images/Card4.jpg
Normal file
After Width: | Height: | Size: 6.1 KiB |
BIN
Client-Side/static/images/card1.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
Client-Side/static/images/card11.png
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
Client-Side/static/images/card2.png
Normal file
After Width: | Height: | Size: 44 KiB |
BIN
Client-Side/static/images/card22.png
Normal file
After Width: | Height: | Size: 136 KiB |
BIN
Client-Side/static/images/card3.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
Client-Side/static/images/card4.png
Normal file
After Width: | Height: | Size: 42 KiB |
BIN
Client-Side/static/images/geometry2.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
Client-Side/static/images/icon.png
Normal file
After Width: | Height: | Size: 113 KiB |
BIN
Client-Side/static/images/iconG.png
Normal file
After Width: | Height: | Size: 114 KiB |
BIN
Client-Side/static/images/image.jpg
Normal file
After Width: | Height: | Size: 66 KiB |
BIN
Client-Side/static/images/test.jpg
Normal file
After Width: | Height: | Size: 12 KiB |
124
Client-Side/static/js/main.js
Normal file
|
@ -0,0 +1,124 @@
|
|||
//config paticle js library
|
||||
particlesJS("particles-js", {
|
||||
particles: {
|
||||
number: {
|
||||
value: 100,
|
||||
density: {
|
||||
enable: true,
|
||||
value_area: 800,
|
||||
},
|
||||
},
|
||||
color: {
|
||||
value: "#ffffff",
|
||||
},
|
||||
shape: {
|
||||
type: "circle",
|
||||
stroke: {
|
||||
width: 0,
|
||||
color: "#000000",
|
||||
},
|
||||
polygon: {
|
||||
nb_sides: 5,
|
||||
},
|
||||
image: {
|
||||
src: "img/github.svg",
|
||||
width: 100,
|
||||
height: 100,
|
||||
},
|
||||
},
|
||||
opacity: {
|
||||
value: 0.5,
|
||||
random: false,
|
||||
anim: {
|
||||
enable: false,
|
||||
speed: 1,
|
||||
opacity_min: 0.1,
|
||||
sync: false,
|
||||
},
|
||||
},
|
||||
size: {
|
||||
value: 3,
|
||||
random: true,
|
||||
anim: {
|
||||
enable: false,
|
||||
speed: 40,
|
||||
size_min: 0.1,
|
||||
sync: false,
|
||||
},
|
||||
},
|
||||
line_linked: {
|
||||
enable: true,
|
||||
distance: 150,
|
||||
color: "#ffffff",
|
||||
opacity: 0.4,
|
||||
width: 1,
|
||||
},
|
||||
move: {
|
||||
enable: true,
|
||||
speed: 6,
|
||||
direction: "none",
|
||||
random: false,
|
||||
straight: false,
|
||||
out_mode: "out",
|
||||
bounce: false,
|
||||
attract: {
|
||||
enable: false,
|
||||
rotateX: 600,
|
||||
rotateY: 1200,
|
||||
},
|
||||
},
|
||||
},
|
||||
interactivity: {
|
||||
detect_on: "canvas",
|
||||
events: {
|
||||
onhover: {
|
||||
enable: true,
|
||||
mode: "grab",
|
||||
},
|
||||
onclick: {
|
||||
enable: true,
|
||||
mode: "push",
|
||||
},
|
||||
resize: true,
|
||||
},
|
||||
modes: {
|
||||
grab: {
|
||||
distance: 140,
|
||||
line_linked: {
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
bubble: {
|
||||
distance: 400,
|
||||
size: 40,
|
||||
duration: 2,
|
||||
opacity: 8,
|
||||
speed: 3,
|
||||
},
|
||||
repulse: {
|
||||
distance: 200,
|
||||
duration: 0.4,
|
||||
},
|
||||
push: {
|
||||
particles_nb: 4,
|
||||
},
|
||||
remove: {
|
||||
particles_nb: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
retina_detect: true,
|
||||
});
|
||||
|
||||
|
||||
|
||||
function showRules() {
|
||||
let x = document.getElementById("rules__text");
|
||||
if (x.style.display === "none") {
|
||||
x.style.display = "block";
|
||||
} else {
|
||||
x.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
// numberOfRounds.addEventListener("input", rangeValue);
|
230
Client-Side/static/js/new-g.js
Normal file
|
@ -0,0 +1,230 @@
|
|||
|
||||
var h5 = document.getElementsByTagName('h5')[0];
|
||||
// var start = document.getElementById('strt');
|
||||
// var stop = document.getElementById('stp');
|
||||
// var reset = document.getElementById('rst');
|
||||
|
||||
var score = 0;
|
||||
var elScore = document.getElementById('score');
|
||||
|
||||
|
||||
var sec = localStorage.getItem('globalTime');
|
||||
var min = 0;
|
||||
var hrs = 0;
|
||||
var t;
|
||||
|
||||
function tick(){
|
||||
|
||||
if( sec > 0){
|
||||
sec--;
|
||||
if(sec == 0){
|
||||
sec = 0;
|
||||
min = 0;
|
||||
hrs = 0;
|
||||
ress = confirm("This part is finished! YOU SCORE IS:" + score + "Do you want to spot it again?");
|
||||
if(ress){
|
||||
window.location.href = "http://127.0.0.1:5000/rules";
|
||||
}
|
||||
else{
|
||||
window.location.href = "http://127.0.0.1:5000/";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function add() {
|
||||
tick();
|
||||
h5.textContent = (hrs > 9 ? hrs : "0" + hrs)
|
||||
+ ":" + (min > 9 ? min : "0" + min)
|
||||
+ ":" + (sec > 9 ? sec : "0" + sec);
|
||||
timer();
|
||||
}
|
||||
|
||||
function timer() {
|
||||
t = setTimeout(add, 1000);
|
||||
}
|
||||
|
||||
timer();
|
||||
// start.onclick = timer;
|
||||
// stop.onclick = function() {
|
||||
// clearTimeout(t);
|
||||
// }
|
||||
// reset.onclick = function() {
|
||||
// h5.textContent = "00:00:00";
|
||||
// seconds = 0; minutes = 0; hours = 0;
|
||||
// }
|
||||
|
||||
var httpRequest;
|
||||
|
||||
// document.getElementById('clickme').onclick =
|
||||
|
||||
|
||||
// var card1 = document.getElementById('clickme');
|
||||
// var card2 = document.getElementById('card2');
|
||||
// var card3 = document.getElementById('card3');
|
||||
// var card4 = document.getElementById('card4');
|
||||
|
||||
|
||||
|
||||
var card1 = document.getElementById('clickme');
|
||||
var card2 = document.getElementById('card2');
|
||||
var card3 = document.getElementById('card3');
|
||||
var card4 = document.getElementById('card4');
|
||||
|
||||
|
||||
function clickEvent(e) {
|
||||
// e = Mouse click event.
|
||||
var rect = e.target.getBoundingClientRect();
|
||||
var x = e.clientX - rect.left; //x position within the element.
|
||||
var y = e.clientY - rect.top; //y position within the element.
|
||||
|
||||
// localStorage.setItem('xPosition', x);
|
||||
// localStorage.setItem('yPosition', y);
|
||||
|
||||
|
||||
|
||||
//console.log("OK");
|
||||
httpRequest = new XMLHttpRequest();
|
||||
|
||||
if(!httpRequest) {
|
||||
alert('Giving Up! Cannot send an XMLHTTO request.');
|
||||
}
|
||||
|
||||
httpRequest.open('POST', 'http://127.0.0.1:5000/apitest');
|
||||
//console.log("OK");
|
||||
var data = JSON.stringify({X: x});
|
||||
//console.log("OK");
|
||||
httpRequest.send(data);
|
||||
//console.log("OK");
|
||||
//console.log(data)
|
||||
|
||||
console.log("Left? : " + x + " ; Top? : " + y + ".");
|
||||
|
||||
|
||||
if( x >= 161 && x <= 219 && y >= 50 && y <= 67 ){
|
||||
|
||||
score++;
|
||||
elScore.textContent = score;
|
||||
ress = confirm("GOOD You win :) DO YOU WANT TO PLAY AGAIN ?");
|
||||
|
||||
if(ress){
|
||||
|
||||
|
||||
card1.style.display = "none";
|
||||
card2.style.display = "block";
|
||||
card3.style.display = "none";
|
||||
card4.style.display = "block";
|
||||
sec = localStorage.getItem('globalTime');
|
||||
}
|
||||
else{
|
||||
window.location.href = "http://127.0.0.1:5000/";
|
||||
}
|
||||
}
|
||||
|
||||
// var xhr = new XMLHttpRequest();
|
||||
// xhr.open("POST", yourUrl, true);
|
||||
// xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
// xhr.send(JSON.stringify({
|
||||
// x: x,
|
||||
// y: y
|
||||
// }))
|
||||
}
|
||||
|
||||
function restartGame() {
|
||||
window.location.href = "http://127.0.0.1:5000/rules";
|
||||
}
|
||||
|
||||
|
||||
|
||||
function clickEvent2(e) {
|
||||
|
||||
var rect = e.target.getBoundingClientRect();
|
||||
var x = e.clientX - rect.left;
|
||||
var y = e.clientY - rect.top;
|
||||
|
||||
httpRequest = new XMLHttpRequest();
|
||||
|
||||
if(!httpRequest) {
|
||||
alert('Giving Up! Cannot send an XMLHTTO request.');
|
||||
}
|
||||
httpRequest.open('POST', 'http://127.0.0.1:5000/apitest');
|
||||
var data = JSON.stringify({X: x});
|
||||
httpRequest.send(data);
|
||||
console.log("Left? : " + x + " ; Top? : " + y + ".");
|
||||
if( x >= 126 && x <= 218 && y >= 26 && y <= 276 ){
|
||||
score++;
|
||||
elScore.textContent = score;
|
||||
ress = confirm("GOOD You win :) DO YOU WANT TO PLAY AGAIN ?");
|
||||
|
||||
if(ress){
|
||||
sec = localStorage.getItem('globalTime');
|
||||
}
|
||||
else{
|
||||
window.location.href = "http://127.0.0.1:5000/";
|
||||
}
|
||||
}
|
||||
|
||||
// var xhr = new XMLHttpRequest();
|
||||
// xhr.open("POST", yourUrl, true);
|
||||
// xhr.setRequestHeader('Content-Type', 'application/json');
|
||||
// xhr.send(JSON.stringify({
|
||||
// x: x,
|
||||
// y: y
|
||||
// }))
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// // Diaporamaa : --------------------------------------
|
||||
|
||||
// var diaporama = 1;
|
||||
// affichage(diaporama);
|
||||
|
||||
// function boutons(n) {
|
||||
// affichage(diaporama += n);
|
||||
// }
|
||||
|
||||
// function actifIndic(n) {
|
||||
// affichage(diaporama = n);
|
||||
// }
|
||||
|
||||
// function affichage(n) {
|
||||
// var i;
|
||||
// var diapoImg = document.getElementsByClassName("diapo");
|
||||
|
||||
// if (n > diapoImg.length) {diaporama = 1}
|
||||
// if (n < 1) {diaporama = diapoImg.length}
|
||||
// for (i = 0; i < diapoImg.length; i++) {
|
||||
|
||||
// diapoImg[i].style.opacity = "0";
|
||||
// }
|
||||
// diapoImg[diaporama-1].style.opacity = "1";
|
||||
|
||||
// }
|
1534
Client-Side/static/js/particles.js
Normal file
26
Client-Side/static/js/rules.js
Normal file
|
@ -0,0 +1,26 @@
|
|||
var slider = document.getElementById("customRange1");
|
||||
var output = document.getElementById("time");
|
||||
output.innerHTML = slider.value + "s"; // Display the default slider value
|
||||
|
||||
var httpRequest;
|
||||
|
||||
// Update the current slider value (each time you drag the slider handle)
|
||||
slider.oninput = function() {
|
||||
output.innerHTML = this.value + " s";
|
||||
}
|
||||
|
||||
function getTime(){
|
||||
var sec = document.getElementById("customRange1").value;
|
||||
|
||||
httpRequest = new XMLHttpRequest();
|
||||
if(!httpRequest) {
|
||||
alert('Giving Up! Cannot send an XMLHTTO request.');
|
||||
}
|
||||
|
||||
httpRequest.open('POST', 'http://192.168.37.69:50001/time');
|
||||
var data = JSON.stringify({"time": sec});
|
||||
|
||||
httpRequest.send(data);
|
||||
localStorage.setItem('globalTime', sec);
|
||||
console.log(sec);
|
||||
}
|
4
Client-Side/static/js/test.js
Normal file
|
@ -0,0 +1,4 @@
|
|||
output = 4;
|
||||
function so() {
|
||||
return output;
|
||||
}
|
64
Client-Side/templates/layout.html
Normal file
|
@ -0,0 +1,64 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
|
||||
integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
|
||||
<link rel="stylesheet" type="text/css" href="../static/css/desing.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Iceland&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=VT323&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300&display=swap" rel="stylesheet">
|
||||
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"
|
||||
integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
|
||||
<link rel="stylesheet prefetch" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.1/css/font-awesome.min.css">
|
||||
<link rel="stylesheet prefetch" href="https://fonts.googleapis.com/css?family=Coda">
|
||||
<link rel="icon" href="{{ url_for('static', filename='../static/images/icon.png') }}" type="image/x-icon">
|
||||
<title>Crads's game</title>
|
||||
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.4.1.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="wrapper">
|
||||
<main class="content ">
|
||||
|
||||
<!-- Display high score -->
|
||||
<a href="/">
|
||||
<div class="logo">< Cards's Game ></div>
|
||||
</a>
|
||||
|
||||
{% block body %}
|
||||
<!-- Body is specified in templates -->
|
||||
{% endblock %}
|
||||
|
||||
<!-- Authors -->
|
||||
<div id="credits">
|
||||
<div>
|
||||
<a href="https://www.linkedin.com/in/ibayova/" target="_blank">
|
||||
<i class="fa fa-hashtag"></i>
|
||||
</a>
|
||||
Recognition and Classification Of Objects
|
||||
</div>
|
||||
<div class="ml-5">
|
||||
<a href="https://www.linkedin.com/in/jakob-schmitt-048179196/" target="_blank">
|
||||
<i class="fa fa-hand-paper-o"></i>
|
||||
</a>
|
||||
Artificial Intelligence; Machine Learning; Convolutional Neural Network; Images Detection; Python...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
<div id="particles-js"></div>
|
||||
|
||||
{% block scripts %}
|
||||
{% endblock %}
|
||||
|
||||
</div>
|
||||
<script src="{{ url_for('static', filename='js/particles.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
48
Client-Side/templates/main.html
Normal file
|
@ -0,0 +1,48 @@
|
|||
{% extends "layout.html" %}
|
||||
|
||||
{% block body %}
|
||||
|
||||
<!-- Option to extend rules -->
|
||||
<div class="content__rules">
|
||||
<div class="info">
|
||||
<button id="rules__btn" class="" onclick="showRules()">
|
||||
<i class="fa fa-info icon-info-sign" aria-hidden="true"></i>
|
||||
<span class="extra-info">
|
||||
CHECK OUT RULES
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Welcome message -->
|
||||
<div class="content__container index__container">
|
||||
<div class="screen-alert">
|
||||
<div id="message">
|
||||
<div class="" role="alert">
|
||||
Welcome to the game!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h3 id="index__slogan" class="mt-4 mb-3 explanations">Can you detect the common object quickly?</h3>
|
||||
<h4 id="index__slogan" class="mt-4 mb-3 explanations">Check out the rules on the top left info button!</h4>
|
||||
|
||||
<form action="/choose-game", method="post">
|
||||
<button class="content__btn">PLay the game !</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<!-- Explanation of rules -->
|
||||
<div id="rules__text" class="content__container index__container mt-3 mb-5" style="display:none;">
|
||||
<div>
|
||||
<img class="my-5 explanations__img" src="" alt="Rule 1 ..." style="width:50%">
|
||||
<p class="explanations">Choose the time of the part..</p>
|
||||
<img class="my-5 explanations__img" src="" alt="Rule 2 ..." style="width:50%">
|
||||
<p class="explanations">Click on the top card to detect the commun object with the bottom card..</p>
|
||||
<img class="my-5 explanations__img" src="" alt="Rule 3 ..." style="width:50%">
|
||||
<p class="explanations">------------------</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
27
Client-Side/templates/menu.html
Normal file
|
@ -0,0 +1,27 @@
|
|||
{% extends "layout.html" %}
|
||||
|
||||
{% block body %}
|
||||
|
||||
<div class="bloc">
|
||||
<h2 style="color: #fff;">Choose an available game</h2>
|
||||
<div class="select">
|
||||
<select>
|
||||
<option>Available modes</option>
|
||||
<option value="1">Mode 1</option>
|
||||
</select>
|
||||
</div>
|
||||
<!-- <div class="imgGame">
|
||||
<img src="../static/images/iconG.png" alt="Our game..." style="border-radius: 50%;">
|
||||
</div> -->
|
||||
<div class="text-box" style="margin-left: 40px; margin-top: 55px; margin-bottom: 0;">
|
||||
<a href="/rules" class="btn btn1 btn-white btn-animate">Play a new game</a>
|
||||
</div>
|
||||
<div class="text-box" style="margin-right: 355px; margin-bottom: 0;">
|
||||
<a href="/test" class="btn btn2 btn-white btn-animate">Join a game</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
68
Client-Side/templates/new-game.html
Normal file
|
@ -0,0 +1,68 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Cards's Game</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet prefetch" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.1/css/font-awesome.min.css">
|
||||
<link rel="stylesheet prefetch" href="https://fonts.googleapis.com/css?family=Coda">
|
||||
<link rel="stylesheet" href="../static/css/new-g.css">
|
||||
<style>
|
||||
body {
|
||||
background: #ffffff url('../static/images/geometry2.png'); /* Background pattern from Subtle Patterns */
|
||||
font-family: 'Coda', cursive;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>Fast Detection Game</h1>
|
||||
</header>
|
||||
<section class="score-panel">
|
||||
<ul class="stars">
|
||||
<li>
|
||||
<i class="fa fa-star gold-star"></i>
|
||||
</li>
|
||||
</ul>
|
||||
<span class="moves">Score :</span><span id="score"> 0</span>
|
||||
<div class="restart">
|
||||
<i class="fa fa-repeat" onclick="restartGame()"></i> Restart game
|
||||
</div>
|
||||
</section>
|
||||
<div id="basicUsage" style="text-align: center;"><i style="display: inline-block; margin-right: 6px;" class="fa fa-clock-o"></i><h5 style="display: inline-block;" ><time></time></h5></div>
|
||||
<ul class="deck1" style="justify-content: center; display: flex; position: relative;">
|
||||
<h4 style="position: absolute; color: white; top: -15px; left: 32%; width: 100%;">Click on the common object here</h4>
|
||||
<img src="../static/images/card11.png" alt="simple card" id="clickme" style="cursor: pointer; margin-top: 10px;" onclick="clickEvent(event)">
|
||||
<!-- <img src="../static/images/card1.png" alt="simple card" id="clickme" style="cursor: pointer; margin-top: 10px;" onclick="clickEvent(event)">
|
||||
<img src="../static/images/card3.png" alt="simple card" id="card2" style="cursor: pointer; margin-top: 10px; display: none;" onclick="clickEvent2(event)"> -->
|
||||
<!-- <img src="../static/images/icon.png" alt="simple card" class="gcard diapo" onclick="boutons(-1)"> -->
|
||||
</ul>
|
||||
|
||||
<hr style="border: #44464778 dashed 1px; width: 80%; margin: 19px 0 9px;">
|
||||
|
||||
<ul class="deck2" style="justify-content: center; display: flex; position: relative;">
|
||||
<h4 style="position: absolute; color: white; top: -8px; left: 42%; width: 100%;">Central Card</h4>
|
||||
<img src="../static/images/card22.png" alt="simple card" id="clickme" style="cursor: pointer; margin-top: 10px;" onclick="clickEvent(event)">
|
||||
<!-- <img src="../static/images/card2.png" id="card3" alt="simple card" style="margin-top: 10px;">
|
||||
<img src="../static/images/card1.png" alt="simple card" id="card4" style="cursor: pointer; margin-top: 10px; display: none;" onclick="clickEvent(event)"> -->
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
<div class="popup">
|
||||
<div class="content">
|
||||
<span class="close">×</span>
|
||||
<h1>Congratulations,</h1>
|
||||
<p>You completed the game in time
|
||||
<span id="timer"> 00:00:00 </span> with total number of moves:
|
||||
<span id="moves">0</span>
|
||||
<br/> your star rating:
|
||||
<span id="rating">0</span> out of 3</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="../static/js/new-g.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
109
Client-Side/templates/rules.html
Normal file
|
@ -0,0 +1,109 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Cards's Game</title>
|
||||
<meta name="description" content="">
|
||||
<link rel="stylesheet prefetch" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.1/css/font-awesome.min.css">
|
||||
<link rel="stylesheet prefetch" href="https://fonts.googleapis.com/css?family=Coda">
|
||||
<link rel="stylesheet" href="../static/css/desing.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Iceland&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=VT323&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300&display=swap" rel="stylesheet">
|
||||
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"
|
||||
integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
|
||||
<link rel="stylesheet prefetch" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.1/css/font-awesome.min.css">
|
||||
<link rel="stylesheet prefetch" href="https://fonts.googleapis.com/css?family=Coda">
|
||||
<style>
|
||||
body {
|
||||
background: #ffffff url('../static/images/geometry2.png'); /* Background pattern from Subtle Patterns */
|
||||
font-family: 'Coda', cursive;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 18px;
|
||||
font-family: "VT323", monospace;
|
||||
line-height: 1.42;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center
|
||||
}
|
||||
|
||||
.content__container {
|
||||
padding: 40px;
|
||||
border-radius: 10px;
|
||||
max-width: 70%;
|
||||
text-align: center;
|
||||
;
|
||||
}
|
||||
.index__container {
|
||||
width: 50%;
|
||||
}
|
||||
#rules__text {
|
||||
/* background-color: rgba(27, 27, 27, 0.2); */
|
||||
box-shadow: 12px 15px 20px 0 rgba(46, 61, 73, 0.5);
|
||||
background: linear-gradient(160deg, rgba(46, 61, 73, 0.5) 0%, rgba(224, 229, 233, 0.5) 100%);
|
||||
|
||||
}
|
||||
.__btn {
|
||||
padding: 15px 25px;
|
||||
margin-top: 20px;
|
||||
background-color: #ffffff;
|
||||
text-transform: uppercase;
|
||||
box-shadow: 0px 6px 0px 0px #6b6b6b, 0px 5px 12px 0px rgba(43, 43, 43, 0.6),
|
||||
inset 0px 0px 10px -5px #1c2a3a;
|
||||
transition: all 100ms linear;
|
||||
cursor: pointer;
|
||||
border-radius: 5px;
|
||||
border: none;
|
||||
height: 60px;
|
||||
color: rgb(51, 51, 51);
|
||||
}
|
||||
.__btn:hover {
|
||||
top: 3px;
|
||||
left: -3px;
|
||||
box-shadow: 0px 2px 0px 0px #7690d6, 0px 5px 5px 0px rgba(0, 0, 0, 0.6),
|
||||
inset 0px 0px 10px -5px #1c2a3a;
|
||||
}
|
||||
.logo2 {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 20px;
|
||||
}
|
||||
.logo2 > img {
|
||||
box-shadow: 0px 2px 0px 0px #383838, 0px 5px 5px 0px rgba(0, 0, 0, 0.6),
|
||||
inset 0px 0px 10px -5px #1c2a3a;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<a href="/">
|
||||
<div class="logo" style="color: rgba(35, 35, 36, 0.548);">< Cards's Game ></div>
|
||||
</a>
|
||||
<div class="logo2">
|
||||
<img src="../static/images/iconG.png" alt="Games'Icon.." style="width: 80px; height: 80px; border-radius: 50%;">
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Explanation of rules -->
|
||||
<div id="rules__text" class="content__container index__container mt-3 mb-5">
|
||||
<h3 style="color: rgb(37, 37, 37);"> Remeber the game rules..</h3>
|
||||
<div>
|
||||
<p class="explanations" style="color: rgb(83, 83, 87);"><i class="fa fa-gavel" aria-hidden="true"></i> Rule 1 : Choose the necessary time to play the part ..</p>
|
||||
<!-- <img class="my-5 explanations__img" src="" alt="" style="width:50%"> -->
|
||||
<p class="explanations" style="color: rgb(83, 83, 87);"><i class="fa fa-gavel" aria-hidden="true"></i> Rule 2 : Click on the top card to detect the common object with the bottom card..</p>
|
||||
<p class="explanations" style="color: rgb(83, 83, 87);"><i class="fa fa-gavel" aria-hidden="true"></i> Rule 3 : Accept the challenge to start the game, the goog luck to Spot it :)</p>
|
||||
<img class="my-5 explanations__img" src="" alt="" style="width:30%">
|
||||
</div>
|
||||
<h3 style="color: rgb(37, 37, 37);"> Choose the time to solve it :)</h3>
|
||||
<label for="customRange1" class="form-label" id="time"></label>
|
||||
<input type="range" class="form-range" id="customRange1" style="background-color: rgba(62, 63, 63, 0.479);">
|
||||
<form action="/new-game", method="post">
|
||||
<button class="__btn" onclick="getTime()">Spot it!</button>
|
||||
</form>
|
||||
</div>
|
||||
<script src="../static/js/rules.js"></script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
104
Client-Side/templates/test.html
Normal file
|
@ -0,0 +1,104 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Cards's Game</title>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<canvas id="canvas" width="150" height="150"></canvas>
|
||||
<div id="status"></div><br>
|
||||
<div id="color" style="width:30px;height:30px;"></div>
|
||||
<p>
|
||||
---- POSITION and COLOR ----
|
||||
</p>
|
||||
|
||||
|
||||
<p>
|
||||
{{X}}
|
||||
</p>
|
||||
|
||||
|
||||
<script>
|
||||
var canvas = document.getElementById("canvas");
|
||||
|
||||
function getElementPosition(obj) {
|
||||
var curleft = 0, curtop = 0;
|
||||
if (obj.offsetParent) {
|
||||
do {
|
||||
curleft += obj.offsetLeft;
|
||||
curtop += obj.offsetTop;
|
||||
} while (obj = obj.offsetParent);
|
||||
return { x: curleft, y: curtop };
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getEventLocation(element,event){
|
||||
var pos = getElementPosition(element);
|
||||
|
||||
return {
|
||||
x: (event.pageX - pos.x),
|
||||
y: (event.pageY - pos.y)
|
||||
};
|
||||
}
|
||||
|
||||
function rgbToHex(r, g, b) {
|
||||
if (r > 255 || g > 255 || b > 255)
|
||||
throw "Invalid color component";
|
||||
return ((r << 16) | (g << 8) | b).toString(16);
|
||||
}
|
||||
|
||||
function drawImageFromWebUrl(sourceurl){
|
||||
var img = new Image();
|
||||
|
||||
img.addEventListener("load", function () {
|
||||
// The image can be drawn from any source
|
||||
canvas.getContext("2d").drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);
|
||||
|
||||
});
|
||||
|
||||
img.setAttribute("src", sourceurl);
|
||||
}
|
||||
// Draw a base64 image because this is a fiddle, and if we try with an image from URL we'll get tainted canvas error
|
||||
// Read more about here : http://ourcodeworld.com/articles/read/182/the-canvas-has-been-tainted-by-cross-origin-data-and-tainted-canvases-may-not-be-exported
|
||||
drawImageFromWebUrl("../static/images/test.jpg");
|
||||
|
||||
canvas.addEventListener("mousemove",function(e){
|
||||
var eventLocation = getEventLocation(this,e);
|
||||
var coord = "x=" + eventLocation.x + ", y=" + eventLocation.y;
|
||||
|
||||
// Get the data of the pixel according to the location generate by the getEventLocation function
|
||||
var context = this.getContext('2d');
|
||||
var pixelData = context.getImageData(eventLocation.x, eventLocation.y, 1, 1).data;
|
||||
|
||||
// If transparency on the image
|
||||
if((pixelData[0] == 0) && (pixelData[1] == 0) && (pixelData[2] == 0) && (pixelData[3] == 0)){
|
||||
coord += " (Transparent color detected, cannot be converted to HEX)";
|
||||
}
|
||||
|
||||
var hex = "#" + ("000000" + rgbToHex(pixelData[0], pixelData[1], pixelData[2])).slice(-6);
|
||||
|
||||
// Draw the color and coordinates.
|
||||
document.getElementById("status").innerHTML = coord;
|
||||
document.getElementById("color").style.backgroundColor = hex;
|
||||
},false);
|
||||
|
||||
|
||||
|
||||
document.getElementById('logout').onclick = function logout() {
|
||||
let token = localStorage.getItem('globalTime')
|
||||
|
||||
// use your favourite AJAX lib to send the token to the server as e.g. JSON
|
||||
|
||||
// redirect user to e.g. landing page of app if logout successul, show error otherwise
|
||||
}
|
||||
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
44
Client-Side/utils/cardToObjects.py
Normal file
|
@ -0,0 +1,44 @@
|
|||
#!pip install imutils
|
||||
#!pip install opencv-python
|
||||
import cv2
|
||||
import imutils
|
||||
import numpy as np
|
||||
|
||||
imgname = 'picture1'
|
||||
|
||||
final = cv2.imread("../static/images/test.jpg")
|
||||
|
||||
def card_to_objects():
|
||||
# just like before (with detecting the card)
|
||||
gray = cv2.cvtColor(final, cv2.COLOR_RGB2GRAY)
|
||||
thresh = cv2.threshold(gray, 195, 255, cv2.THRESH_BINARY)[1]
|
||||
thresh = cv2.bitwise_not(thresh)
|
||||
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||
cnts = imutils.grab_contours(cnts)
|
||||
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:10]# handle each contour
|
||||
i = 0
|
||||
for c in cnts:
|
||||
if cv2.contourArea(c) > 1000:
|
||||
# draw mask, keep contour
|
||||
mask = np.zeros(gray.shape, np.uint8)
|
||||
mask = cv2.drawContours(mask, [c], -1, 255, cv2.FILLED) # white background
|
||||
fg_masked = cv2.bitwise_and(final, final, mask=mask)
|
||||
mask = cv2.bitwise_not(mask)
|
||||
bk = np.full(final.shape, 255, dtype=np.uint8)
|
||||
bk_masked = cv2.bitwise_and(bk, bk, mask=mask)
|
||||
finalcont = cv2.bitwise_or(fg_masked, bk_masked) # bounding rectangle around contour
|
||||
output = finalcont.copy()
|
||||
x,y,w,h = cv2.boundingRect(c)
|
||||
# squares io rectangles
|
||||
if w < h:
|
||||
x += int((w-h)/2)
|
||||
w = h
|
||||
else:
|
||||
y += int((h-w)/2)
|
||||
h = w # take out the square with the symbol
|
||||
roi = finalcont[y:y+h, x:x+w]
|
||||
roi = cv2.resize(roi, (400,400)) # save the symbol
|
||||
cv2.imwrite(f"{imgname}_icon{i}.jpg", roi)
|
||||
i += 1
|
||||
|
||||
card_to_objects()
|
15
Client-Side/utils/ports.py
Normal file
|
@ -0,0 +1,15 @@
|
|||
from flask import Flask, render_template, jsonify
|
||||
import requests
|
||||
|
||||
#GET PORTS :------------------------------------------------------
|
||||
|
||||
API_URL2 = "http://192.168.37.69:50000/port"
|
||||
|
||||
port = 0
|
||||
|
||||
def getPort():
|
||||
res = requests.get(API_URL2)
|
||||
port = res.json()
|
||||
return port.get('numport') # our port
|
||||
|
||||
port = getPort()
|
20
Client-Side/utils/test.py
Normal file
|
@ -0,0 +1,20 @@
|
|||
# import js2py
|
||||
|
||||
# js = """../static/js/test.js"""
|
||||
|
||||
# context = js2py.EvalJs()
|
||||
# context.execute(js)
|
||||
# print(context.output)
|
||||
import execjs
|
||||
|
||||
|
||||
print(execjs.eval("'red yellow blue'.split(' ')"))
|
||||
|
||||
ctx = execjs.compile("""
|
||||
function add(x, y) {
|
||||
return x + y;
|
||||
}
|
||||
|
||||
""")
|
||||
|
||||
print(ctx.call("add", 1, 2))
|
241
Client-Side/venv/bin/Activate.ps1
Normal file
|
@ -0,0 +1,241 @@
|
|||
<#
|
||||
.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.
|
||||
|
||||
.Notes
|
||||
On Windows, it may be required to enable this Activate.ps1 script by setting the
|
||||
execution policy for the user. You can do this by issuing the following PowerShell
|
||||
command:
|
||||
|
||||
PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
|
||||
For more information on Execution Policies:
|
||||
https://go.microsoft.com/fwlink/?LinkID=135170
|
||||
|
||||
#>
|
||||
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("\\/")
|
||||
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"
|
76
Client-Side/venv/bin/activate
Normal file
|
@ -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="/home/bachar/Bureau/Integrated Project/client-project/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 https://aspen.io/
|
||||
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
|
37
Client-Side/venv/bin/activate.csh
Normal file
|
@ -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 <davidedb@gmail.com>.
|
||||
# Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com>
|
||||
|
||||
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 "/home/bachar/Bureau/Integrated Project/client-project/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 https://aspen.io/
|
||||
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
|
75
Client-Side/venv/bin/activate.fish
Normal file
|
@ -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 "/home/bachar/Bureau/Integrated Project/client-project/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 https://aspen.io/
|
||||
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
|
10
Client-Side/venv/bin/flask
Executable file
|
@ -0,0 +1,10 @@
|
|||
#!/bin/sh
|
||||
'''exec' "/home/bachar/Bureau/Integrated Project/client-project/venv/bin/python3" "$0" "$@"
|
||||
' '''
|
||||
# -*- 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())
|
10
Client-Side/venv/bin/pip
Executable file
|
@ -0,0 +1,10 @@
|
|||
#!/bin/sh
|
||||
'''exec' "/home/bachar/Bureau/Integrated Project/client-project/venv/bin/python3" "$0" "$@"
|
||||
' '''
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pip._internal.cli.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
10
Client-Side/venv/bin/pip3
Executable file
|
@ -0,0 +1,10 @@
|
|||
#!/bin/sh
|
||||
'''exec' "/home/bachar/Bureau/Integrated Project/client-project/venv/bin/python3" "$0" "$@"
|
||||
' '''
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pip._internal.cli.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
10
Client-Side/venv/bin/pip3.8
Executable file
|
@ -0,0 +1,10 @@
|
|||
#!/bin/sh
|
||||
'''exec' "/home/bachar/Bureau/Integrated Project/client-project/venv/bin/python3" "$0" "$@"
|
||||
' '''
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pip._internal.cli.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
1
Client-Side/venv/bin/python
Symbolic link
|
@ -0,0 +1 @@
|
|||
python3
|
1
Client-Side/venv/bin/python3
Symbolic link
|
@ -0,0 +1 @@
|
|||
/usr/local/insa/anaconda/bin/python3
|
|
@ -0,0 +1 @@
|
|||
pip
|
|
@ -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.
|
|
@ -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
|
||||
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
../../../bin/flask,sha256=KfdQ7IUrxEtCeg5cCtNcgS_WLjwjR03OTKQok43p4AE,297
|
||||
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/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
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
|
|
@ -0,0 +1,5 @@
|
|||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.37.0)
|
||||
Root-Is-Purelib: true
|
||||
Tag: py3-none-any
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
[console_scripts]
|
||||
flask = flask.cli:main
|
||||
|
|
@ -0,0 +1 @@
|
|||
flask
|
|
@ -0,0 +1 @@
|
|||
pip
|
|
@ -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.
|
|
@ -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 %}
|
||||
<ul>
|
||||
{% for user in users %}
|
||||
<li><a href="{{ user.url }}">{{ user.username }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% 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
|
||||
|
||||
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.37.0)
|
||||
Root-Is-Purelib: true
|
||||
Tag: py3-none-any
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
[babel.extractors]
|
||||
jinja2 = jinja2.ext:babel_extract [i18n]
|
||||
|
|
@ -0,0 +1 @@
|
|||
jinja2
|
|
@ -0,0 +1 @@
|
|||
pip
|
|
@ -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.
|
|
@ -0,0 +1,101 @@
|
|||
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
|
||||
License-File: LICENSE.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("<script>alert(document.cookie);</script>")
|
||||
Markup('<script>alert(document.cookie);</script>')
|
||||
|
||||
>>> # wrap in Markup to mark text "safe" and prevent escaping
|
||||
>>> Markup("<strong>Hello</strong>")
|
||||
Markup('<strong>hello</strong>')
|
||||
|
||||
>>> escape(Markup("<strong>Hello</strong>"))
|
||||
Markup('<strong>hello</strong>')
|
||||
|
||||
>>> # Markup is a str subclass
|
||||
>>> # methods and operators escape their arguments
|
||||
>>> template = Markup("Hello <em>{name}</em>")
|
||||
>>> template.format(name='"World"')
|
||||
Markup('Hello <em>"World"</em>')
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
|
@ -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=lknelt-VPHWai5EJcvZpATGKVbXkg74h7CQuPwDS71U,3237
|
||||
MarkupSafe-2.0.1.dist-info/RECORD,,
|
||||
MarkupSafe-2.0.1.dist-info/WHEEL,sha256=-DoGBj0Avq2--T-QgXLI1C-bokNS_QjsEnIPIG0jMTU,217
|
||||
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-x86_64-linux-gnu.so,sha256=s6hSF-stCrLXV4xN9Mn0ZqYBD7ADdrAY6oMkZTqMH0A,53192
|
||||
markupsafe/_speedups.pyi,sha256=vfMCsOgbAXRNLUXkyuyonG8uEWKYU4PDqNuMaDELAYw,229
|
||||
markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@ -0,0 +1,8 @@
|
|||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.36.2)
|
||||
Root-Is-Purelib: false
|
||||
Tag: cp38-cp38-manylinux_2_5_x86_64
|
||||
Tag: cp38-cp38-manylinux1_x86_64
|
||||
Tag: cp38-cp38-manylinux_2_12_x86_64
|
||||
Tag: cp38-cp38-manylinux2010_x86_64
|
||||
|
|
@ -0,0 +1 @@
|
|||
markupsafe
|
|
@ -0,0 +1 @@
|
|||
pip
|
|
@ -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.
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.37.0)
|
||||
Root-Is-Purelib: true
|
||||
Tag: py3-none-any
|
||||
|
|
@ -0,0 +1 @@
|
|||
werkzeug
|
|
@ -0,0 +1,128 @@
|
|||
import sys
|
||||
import os
|
||||
import re
|
||||
import importlib
|
||||
import warnings
|
||||
|
||||
|
||||
is_pypy = '__pypy__' in sys.builtin_module_names
|
||||
|
||||
|
||||
warnings.filterwarnings('ignore',
|
||||
'.+ distutils .+ deprecated',
|
||||
DeprecationWarning)
|
||||
|
||||
|
||||
def warn_distutils_present():
|
||||
if 'distutils' not in sys.modules:
|
||||
return
|
||||
if is_pypy and sys.version_info < (3, 7):
|
||||
# PyPy for 3.6 unconditionally imports distutils, so bypass the warning
|
||||
# https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250
|
||||
return
|
||||
warnings.warn(
|
||||
"Distutils was imported before Setuptools, but importing Setuptools "
|
||||
"also replaces the `distutils` module in `sys.modules`. This may lead "
|
||||
"to undesirable behaviors or errors. To avoid these issues, avoid "
|
||||
"using distutils directly, ensure that setuptools is installed in the "
|
||||
"traditional way (e.g. not an editable install), and/or make sure "
|
||||
"that setuptools is always imported before distutils.")
|
||||
|
||||
|
||||
def clear_distutils():
|
||||
if 'distutils' not in sys.modules:
|
||||
return
|
||||
warnings.warn("Setuptools is replacing distutils.")
|
||||
mods = [name for name in sys.modules if re.match(r'distutils\b', name)]
|
||||
for name in mods:
|
||||
del sys.modules[name]
|
||||
|
||||
|
||||
def enabled():
|
||||
"""
|
||||
Allow selection of distutils by environment variable.
|
||||
"""
|
||||
which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'stdlib')
|
||||
return which == 'local'
|
||||
|
||||
|
||||
def ensure_local_distutils():
|
||||
clear_distutils()
|
||||
distutils = importlib.import_module('setuptools._distutils')
|
||||
distutils.__name__ = 'distutils'
|
||||
sys.modules['distutils'] = distutils
|
||||
|
||||
# sanity check that submodules load as expected
|
||||
core = importlib.import_module('distutils.core')
|
||||
assert '_distutils' in core.__file__, core.__file__
|
||||
|
||||
|
||||
def do_override():
|
||||
"""
|
||||
Ensure that the local copy of distutils is preferred over stdlib.
|
||||
|
||||
See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401
|
||||
for more motivation.
|
||||
"""
|
||||
if enabled():
|
||||
warn_distutils_present()
|
||||
ensure_local_distutils()
|
||||
|
||||
|
||||
class DistutilsMetaFinder:
|
||||
def find_spec(self, fullname, path, target=None):
|
||||
if path is not None:
|
||||
return
|
||||
|
||||
method_name = 'spec_for_{fullname}'.format(**locals())
|
||||
method = getattr(self, method_name, lambda: None)
|
||||
return method()
|
||||
|
||||
def spec_for_distutils(self):
|
||||
import importlib.abc
|
||||
import importlib.util
|
||||
|
||||
class DistutilsLoader(importlib.abc.Loader):
|
||||
|
||||
def create_module(self, spec):
|
||||
return importlib.import_module('setuptools._distutils')
|
||||
|
||||
def exec_module(self, module):
|
||||
pass
|
||||
|
||||
return importlib.util.spec_from_loader('distutils', DistutilsLoader())
|
||||
|
||||
def spec_for_pip(self):
|
||||
"""
|
||||
Ensure stdlib distutils when running under pip.
|
||||
See pypa/pip#8761 for rationale.
|
||||
"""
|
||||
if self.pip_imported_during_build():
|
||||
return
|
||||
clear_distutils()
|
||||
self.spec_for_distutils = lambda: None
|
||||
|
||||
@staticmethod
|
||||
def pip_imported_during_build():
|
||||
"""
|
||||
Detect if pip is being imported in a build script. Ref #2355.
|
||||
"""
|
||||
import traceback
|
||||
return any(
|
||||
frame.f_globals['__file__'].endswith('setup.py')
|
||||
for frame, line in traceback.walk_stack(None)
|
||||
)
|
||||
|
||||
|
||||
DISTUTILS_FINDER = DistutilsMetaFinder()
|
||||
|
||||
|
||||
def add_shim():
|
||||
sys.meta_path.insert(0, DISTUTILS_FINDER)
|
||||
|
||||
|
||||
def remove_shim():
|
||||
try:
|
||||
sys.meta_path.remove(DISTUTILS_FINDER)
|
||||
except ValueError:
|
||||
pass
|
|
@ -0,0 +1 @@
|
|||
__import__('_distutils_hack').do_override()
|
|
@ -0,0 +1 @@
|
|||
pip
|
|
@ -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.
|
|
@ -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
|
||||
|
||||
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
Wheel-Version: 1.0
|
||||
Generator: bdist_wheel (0.37.0)
|
||||
Root-Is-Purelib: true
|
||||
Tag: py3-none-any
|
||||
|
|
@ -0,0 +1 @@
|
|||
click
|
|
@ -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"
|
627
Client-Side/venv/lib/python3.8/site-packages/click/_compat.py
Normal file
|
@ -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,
|
||||
}
|
|
@ -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
|
|
@ -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)
|
|
@ -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))
|