feat(curriculum): add password generator project to Python curriculum (#52444)

Co-authored-by: Estefania Cassingena Navone <85124911+estefaniacn@users.noreply.github.com>
Co-authored-by: moT01 <20648924+moT01@users.noreply.github.com>
This commit is contained in:
Dario-DC
2023-12-19 23:25:36 -06:00
committed by GitHub
co-authored by Estefania Cassingena Navone moT01
parent 739312c546
commit 973c475d01
74 changed files with 4575 additions and 0 deletions
@@ -0,0 +1,10 @@
---
title: Introduction to the Learn Regular Expressions by Building a Password Generator
block: learn-regular-expressions-by-building-a-password-generator
superBlock: upcoming-python
isBeta: true
---
## Introduction to the Learn Regular Expressions by Building a Password Generator
This is a test for the new project-based curriculum.
@@ -0,0 +1,304 @@
{
"name": "Learn Regular Expressions by Building a Password Generator",
"isUpcomingChange": false,
"usesMultifileEditor": true,
"hasEditableBoundaries": true,
"dashedName": "learn-regular-expressions-by-building-a-password-generator",
"order": 4,
"time": "5 hours",
"template": "",
"required": [],
"superBlock": "scientific-computing-with-python",
"isBeta": true,
"challengeOrder": [
{
"id": "6564642ce403fe29cc23fee7",
"title": "Step 1"
},
{
"id": "6564683821b2ee3174e7250e",
"title": "Step 2"
},
{
"id": "656469e62377ab34a5bcb8d1",
"title": "Step 3"
},
{
"id": "656485a8a3496d4a36b1496a",
"title": "Step 4"
},
{
"id": "65646ab6bced52360f44670f",
"title": "Step 5"
},
{
"id": "6564883669b5af4b69f794cc",
"title": "Step 6"
},
{
"id": "656489e0a5d0954c1a64fc93",
"title": "Step 7"
},
{
"id": "65646ffeaed2d238c562a014",
"title": "Step 8"
},
{
"id": "656470d517833a39bb8b5608",
"title": "Step 9"
},
{
"id": "656474314bf37d3c83a85143",
"title": "Step 10"
},
{
"id": "656475bbf1c2573de1d2c69c",
"title": "Step 11"
},
{
"id": "656477845006313fbfea0ad1",
"title": "Step 12"
},
{
"id": "656479aa5f298441c190bf8f",
"title": "Step 13"
},
{
"id": "65647c71a5d2bd431596f629",
"title": "Step 14"
},
{
"id": "65648e4c5b316c4ec5e4fddc",
"title": "Step 15"
},
{
"id": "65648eefbee2014f5815f4ba",
"title": "Step 16"
},
{
"id": "65648f4b2281ba50051ae39c",
"title": "Step 17"
},
{
"id": "6564a767a7241362f7d8d664",
"title": "Step 18"
},
{
"id": "656490295d346850a4c4f2b5",
"title": "Step 19"
},
{
"id": "65649122c7f77f519aaf0975",
"title": "Step 20"
},
{
"id": "656491fa4e69005287eb5a9a",
"title": "Step 21"
},
{
"id": "656ef54070c72161e6feb90f",
"title": "Step 22"
},
{
"id": "656ef5bc5c0cd464be1df675",
"title": "Step 23"
},
{
"id": "656ef783bba6976de014eaa8",
"title": "Step 24"
},
{
"id": "656ef7f792734072dedd8319",
"title": "Step 25"
},
{
"id": "656ef89b4b486b7a16077864",
"title": "Step 26"
},
{
"id": "656494269ccce754411a2112",
"title": "Step 27"
},
{
"id": "65649a631440b757f9f7b428",
"title": "Step 28"
},
{
"id": "6564b8c9349bd76dc037967b",
"title": "Step 29"
},
{
"id": "656eec2f40d18056cc58b229",
"title": "Step 30"
},
{
"id": "6564c67db26c417561ab510d",
"title": "Step 31"
},
{
"id": "656f04b20397d9a574cc7eb2",
"title": "Step 32"
},
{
"id": "656f0bc259c1f6b57486ed68",
"title": "Step 33"
},
{
"id": "656f0d6bc60b58b9777a36c9",
"title": "Step 34"
},
{
"id": "656f10458134d4c4e283a2f1",
"title": "Step 35"
},
{
"id": "6564cbb6311a40783b3f5de6",
"title": "Step 36"
},
{
"id": "6564cf2e8642517abdf9d6e2",
"title": "Step 37"
},
{
"id": "6564d096a55d707bd77ab67b",
"title": "Step 38"
},
{
"id": "6564d27c8e4c197d367c3b1c",
"title": "Step 39"
},
{
"id": "6564d2eeb36ebe7dd9bd1ee9",
"title": "Step 40"
},
{
"id": "6564d500f1a48e7f2b732a37",
"title": "Step 41"
},
{
"id": "6564d68c34027a8072a704f4",
"title": "Step 42"
},
{
"id": "6564d75a923d21815caaa445",
"title": "Step 43"
},
{
"id": "6564d97a5ef99783216a6229",
"title": "Step 44"
},
{
"id": "6564daa2725b9b8420141b1c",
"title": "Step 45"
},
{
"id": "6564db880cf9408535f17ff4",
"title": "Step 46"
},
{
"id": "6564dd65c3c2fa873a83d213",
"title": "Step 47"
},
{
"id": "6564de10a0887f882b0012d3",
"title": "Step 48"
},
{
"id": "6564eebf0d2d6390b9377197",
"title": "Step 49"
},
{
"id": "6564efa70114b591b74d5679",
"title": "Step 50"
},
{
"id": "6564f0279e23ce924eedd1b2",
"title": "Step 51"
},
{
"id": "6564f32b18480893cf7799fd",
"title": "Step 52"
},
{
"id": "6564f583ca6fbf9556098dd6",
"title": "Step 53"
},
{
"id": "6564f834dd717998092cfd47",
"title": "Step 54"
},
{
"id": "6564fd78ffff909b1531d3c3",
"title": "Step 55"
},
{
"id": "6565012a2564509d40a90048",
"title": "Step 56"
},
{
"id": "6565bb128adfcd5ec362382d",
"title": "Step 57"
},
{
"id": "6565c014db6e9b63c257771d",
"title": "Step 58"
},
{
"id": "6565bd4265158360de8e2ae7",
"title": "Step 59"
},
{
"id": "6565c084627071646f94c4b0",
"title": "Step 60"
},
{
"id": "6577319039f4f7de9251b822",
"title": "Step 61"
},
{
"id": "657732654845d8e2fb1217e6",
"title": "Step 62"
},
{
"id": "6577333feab1e8e927014f03",
"title": "Step 63"
},
{
"id": "657733bc97eb83efdb7e3988",
"title": "Step 64"
},
{
"id": "6565c13fdb798865c161d8f8",
"title": "Step 65"
},
{
"id": "657dadf4d8b93c1704f3a57c",
"title": "Step 66"
},
{
"id": "6565c234de8cdf673c96bdf3",
"title": "Step 67"
},
{
"id": "657db2114b4029241956f5d6",
"title": "Step 68"
},
{
"id": "657db4cb77190e33a20e852a",
"title": "Step 69"
},
{
"id": "6565c32f89ab8d68b42aff30",
"title": "Step 70"
},
{
"id": "6565c3a146bd5469b62bc59e",
"title": "Step 71"
},
{
"id": "6565c4767f49286aec825c6d",
"title": "Step 72"
}
],
"helpCategory": "Python"
}
@@ -0,0 +1,30 @@
---
id: 6564642ce403fe29cc23fee7
title: Step 1
challengeType: 20
dashedName: step-1
---
# --description--
A Python module is a file containing code designed to perform specific tasks. The Python standard library contains many modules that you can import and use in your code. You can achieve this by using the `import` statement followed by the name of the module.
Start this project by importing the `string` module.
# --hints--
You should import the `string` module.
```js
({ test: () => assert.match(code, /^import\s+string/m) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
--fcc-editable-region--
```
@@ -0,0 +1,53 @@
---
id: 6564683821b2ee3174e7250e
title: Step 2
challengeType: 20
dashedName: step-2
---
# --description--
You can access the utilities defined inside the imported module through the dot notation. The dot notation works by appending a dot followed by the utility name to the module name. For example, here's how to access the `ascii_lowercase` constant:
```py
import string
print(string.ascii_lowercase)
# Output: abcdefghijklmnopqrstuvwxyz
```
In this project, you are going to use different constants from the `string` module.
Declare a new variable called `letters` and assign `string.ascii_letters` to this variable.
# --hints--
You should declare a variable named `letters`.
```js
({ test: () => assert(__userGlobals.has("letters")) })
```
You should assign `string.ascii_letters` to your `letters` variable.
```js
({ test: () => assert(__pyodide.runPython(`
import string
abc = __locals.get("letters")
abc == string.ascii_letters
`))
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
import string
--fcc-editable-region--
```
@@ -0,0 +1,58 @@
---
id: 656469e62377ab34a5bcb8d1
title: Step 3
challengeType: 20
dashedName: step-3
---
# --description--
Declare two new variables named `digits` and `symbols` and assign them `string.digits` and `string.punctuation`, respectively.
# --hints--
You should declare a variable named `digits`.
```js
({ test: () => assert(__userGlobals.has("digits")) })
```
You should assign `string.digits` to your `digits` variable.
```js
({ test: () => assert(__pyodide.runPython(`
import string
nums = __locals.get("digits")
nums == string.digits
`))
})
```
You should declare a variable named `symbols`.
```js
({ test: () => assert(__userGlobals.has("symbols")) })
```
You should assign `string.punctuation` to your `symbols` variable.
```js
({ test: () => assert(__pyodide.runPython(`
import string
s = __locals.get("symbols")
s == string.punctuation
`))
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
import string
letters = string.ascii_letters
--fcc-editable-region--
```
@@ -0,0 +1,48 @@
---
id: 65646ab6bced52360f44670f
title: Step 5
challengeType: 20
dashedName: step-5
---
# --description--
Now declare a variable named `all_characters` and assign it the result of concatenating your existing variables.
# --hints--
You should declare a variable named `all_characters`.
```js
({ test: () => assert(__userGlobals.has("all_characters")) })
```
You should concatenate `letters`, `digits`, and `symbols` and assign the result to your `all_characters` variable.
```js
({ test: () => assert(__pyodide.runPython(`
import string
from itertools import permutations
chars = __locals.get("all_characters")
perms = permutations([string.ascii_letters, string.digits, string.punctuation])
any("".join(perm) == chars for perm in perms)
`))
})
```
# --seed--
## --seed-contents--
```py
import string
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
--fcc-editable-region--
--fcc-editable-region--
```
@@ -0,0 +1,41 @@
---
id: 65646ffeaed2d238c562a014
title: Step 8
challengeType: 20
dashedName: step-8
---
# --description--
It is a common convention to place `import` statements at the top of your code. And additionally, in case of multiple `import` statements, sort them in alphabetical order to improve readability.
At the top of your code, import the `random` module.
# --hints--
You should import the `random` module.
```js
({ test: () => assert.match(code, /^import\s+random/m) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
import string
--fcc-editable-region--
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
print(all_characters)
```
@@ -0,0 +1,43 @@
---
id: 656470d517833a39bb8b5608
title: Step 9
challengeType: 20
dashedName: step-9
---
# --description--
The `random` module contains a pseudo-random number generator. Most of its functionalities depend on the `random()` function, which returns a floating point number in the range between `0.0` (inclusive) and `1.0` (exclusive).
Call the `random()` function and print the result.
# --hints--
You should print `random.random()`.
```js
({ test: () => assert.match(code, /^print\s*\(\s*random\.random\s*\(\s*\)\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
import random
import string
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
print(all_characters)
--fcc-editable-region--
--fcc-editable-region--
```
@@ -0,0 +1,43 @@
---
id: 656474314bf37d3c83a85143
title: Step 10
challengeType: 20
dashedName: step-10
---
# --description--
The `choice()` function from the `random` module takes a sequence and returns a random item of the sequence.
Modify your `print()` call to use the `choice()` function and pass `all_characters` as the argument.
# --hints--
You should modify your existing `print()` call to print `random.choice(all_characters)`.
```js
({ test: () => assert.match(code, /^print\s*\(\s*random\.choice\s*\(\s*all_characters\s*\)\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
import random
import string
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
print(all_characters)
--fcc-editable-region--
print(random.random())
--fcc-editable-region--
```
@@ -0,0 +1,56 @@
---
id: 656475bbf1c2573de1d2c69c
title: Step 11
challengeType: 20
dashedName: step-11
---
# --description--
Every time the code runs, you should see a random character from the `all_characters` string. This is exactly what you want to achieve to create a random password.
However, the algorithm on which `random` relies makes the generated pseudo-random numbers predictable. Therefore, although the `random` module is suitable for the most common applications, it cannot be used for cryptographic purposes, due to its deterministic nature.
Instead of importing `random`, import the `secrets` module. Then change the `print()` call to use `secrets.choice(all_characters)`.
# --hints--
You should import the `secrets` module instead of the `random` module.
```js
({
test: () => {
assert.match(code, /^import\s+secrets/m)
assert.isFalse( /^import\s+random/m.test(code))
}
})
```
You should modify your existing `print()` call to print `secrets.choice(all_characters)`.
```js
({ test: () => assert.match(code, /^print\s*\(\s*secrets\.choice\s*\(\s*all_characters\s*\)\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
import random
import string
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
print(all_characters)
print(random.choice(all_characters))
--fcc-editable-region--
```
@@ -0,0 +1,42 @@
---
id: 656477845006313fbfea0ad1
title: Step 12
challengeType: 20
dashedName: step-12
---
# --description--
Although the effect might seem equal to `random.choice()`, `secrets` ensure you the most secure source of randomness that your operating system can provide.
Now, delete your two `print()` calls.
# --hints--
You should delete your two `print()` calls.
```js
({ test: () => assert.isFalse( /print/.test(code)) })
```
# --seed--
## --seed-contents--
```py
import secrets
import string
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
--fcc-editable-region--
print(all_characters)
print(secrets.choice(all_characters))
--fcc-editable-region--
```
@@ -0,0 +1,43 @@
---
id: 656479aa5f298441c190bf8f
title: Step 13
challengeType: 20
dashedName: step-13
---
# --description--
Declare a `generate_password` function and write all your code except the `import` lines inside the function body.
# --hints--
You should declare a function named `generate_password`.
```js
({ test: () => assert(__pyodide.runPython(`
import inspect
generate_psw = __locals.get("generate_password")
inspect.isfunction(generate_psw)
`))
})
```
# --seed--
## --seed-contents--
```py
import secrets
import string
--fcc-editable-region--
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
--fcc-editable-region--
```
@@ -0,0 +1,46 @@
---
id: 65647c71a5d2bd431596f629
title: Step 14
challengeType: 20
dashedName: step-14
---
# --description--
Your `generate_password` function needs a few parameters. Start by adding a `length` parameter.
# --hints--
Your `generate_password` function should take a `length` parameter.
```js
({ test: () => assert(__pyodide.runPython(`
import inspect
generate_psw = __locals.get("generate_password")
sig = str(inspect.signature(generate_psw))
sig == "(length)"
`))
})
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
import secrets
import string
def generate_password():
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
--fcc-editable-region--
```
@@ -0,0 +1,36 @@
---
id: 656485a8a3496d4a36b1496a
title: Step 4
challengeType: 20
dashedName: step-4
---
# --description--
These three variables constitute the possible characters to choose from when generating the password.
Just before them, add a comment saying `Define the possible characters for the password`.
# --hints--
You should add the comment just above your three variables.
```js
({ test: () => assert.match(code, /^#\s*Define the possible characters for the password.*^letters/ms) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
import string
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
--fcc-editable-region--
```
@@ -0,0 +1,39 @@
---
id: 6564883669b5af4b69f794cc
title: Step 6
challengeType: 20
dashedName: step-6
---
# --description--
Your `all_characters` variable is a string formed by all lowercase and uppercase letters, all the 10 digits and several special characters.
Just before it, add a comment saying `Combine all characters`.
# --hints--
You should add the comment just above your `all_characters` variable.
```js
({ test: () => assert.match(code, /^#\s*Combine all characters\s+^all_characters/m) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
import string
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
all_characters = letters + digits + symbols
--fcc-editable-region--
```
@@ -0,0 +1,37 @@
---
id: 656489e0a5d0954c1a64fc93
title: Step 7
challengeType: 20
dashedName: step-7
---
# --description--
Now print the `all_characters` variable to see what it looks like.
# --hints--
You should print your `all_characters` variable.
```js
({ test: () => assert.match(code, /^print\s*\(\s*all_characters\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
import string
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
--fcc-editable-region--
# Combine all characters
all_characters = letters + digits + symbols
--fcc-editable-region--
```
@@ -0,0 +1,47 @@
---
id: 65648e4c5b316c4ec5e4fddc
title: Step 15
challengeType: 20
dashedName: step-15
---
# --description--
At the bottom of your function, declare a `password` variable and assign an empty string to this variable.
# --hints--
You should have a `password` variable inside your function with the value of an empty string. Pay attention to the indentation.
```js
({ test: () =>
{
const transformedCode = code.replace(/\r/g, "");
const generate_pw = __helpers.python.getDef("\n"+transformedCode, "generate_password");
const {function_body} = generate_pw;
assert(function_body.match(/password\s*=\s*("|')\1/));
}
})
```
# --seed--
## --seed-contents--
```py
import secrets
import string
def generate_password(length):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
--fcc-editable-region--
--fcc-editable-region--
```
@@ -0,0 +1,48 @@
---
id: 65648eefbee2014f5815f4ba
title: Step 16
challengeType: 20
dashedName: step-16
---
# --description--
Below your new variable, add a comment saying `Generate password`.
# --hints--
You should add the provided comment.
```js
({ test: () =>
{
const transformedCode = code.replace(/\r/g, "");
const generate_pw = __helpers.python.getDef("\n"+transformedCode, "generate_password");
const {function_body} = generate_pw;
assert(function_body.match(/#\s*Generate password/));
}
})
```
# --seed--
## --seed-contents--
```py
import secrets
import string
def generate_password(length):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
--fcc-editable-region--
password = ''
--fcc-editable-region--
```
@@ -0,0 +1,62 @@
---
id: 65648f4b2281ba50051ae39c
title: Step 17
challengeType: 20
dashedName: step-17
---
# --description--
Next, write a `for` loop with `i` as the loop variable. Use the `range()` function to iterate up to the value of the `length`.
Inside the loop, use the addition assignment operator to add a random character from `all_characters` to the current value of `password`. Use the `choice()` function from the `secrets` module for that.
# --hints--
You should write a `for` loop that iterates over `range(length)`.
```js
({ test: () =>
{
const transformedCode = code.replace(/\r/g, "");
const generate_pw = __helpers.python.getDef("\n"+transformedCode, "generate_password");
const {function_body} = generate_pw;
assert(function_body.match(/for\s+i\s+in\s+range\s*\(\s*length\s*\)\s*:/));
}
})
```
You should use the `+=` operator to add a random character from `all_characters` to the current value of `password`.
```js
({ test: () =>
{
const generate_pwd = __helpers.python.getDef(code, "generate_password");
const {function_body} = generate_pwd;
assert(function_body.match(/^(\s*)for\s+i\s+in\s+range\s*\(\s*length\s*\)\s*:\s*^\1\1password\s*\+=\s*secrets\.choice\s*\(\s*all_characters\s*\)/m));
}
})
```
# --seed--
## --seed-contents--
```py
import secrets
import string
def generate_password(length):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
--fcc-editable-region--
password = ''
# Generate password
--fcc-editable-region--
```
@@ -0,0 +1,51 @@
---
id: 656490295d346850a4c4f2b5
title: Step 19
challengeType: 20
dashedName: step-19
---
# --description--
After the loop, add a `return` statement to your function so it returns the `password` variable.
# --hints--
You should return `password` at the end of your function.
```js
({ test: () =>
{
const transformedCode = code.replace(/\r/g, "");
const generate_pw = __helpers.python.getDef("\n"+transformedCode, "generate_password");
const {function_body} = generate_pw;
assert(function_body.match(/^(\s*)for\s+\w\s+in\s+range\s*\(\s*length\s*\)\s*:.+^\1return\s+password/ms));
}
})
```
# --seed--
## --seed-contents--
```py
import secrets
import string
def generate_password(length):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
--fcc-editable-region--
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
--fcc-editable-region--
```
@@ -0,0 +1,59 @@
---
id: 65649122c7f77f519aaf0975
title: Step 20
challengeType: 20
dashedName: step-20
---
# --description--
Finally, call the `generate_password` function with `8` as the argument and assign the function call to a `new_password` variable.
# --hints--
You should call `generate_password` passing `8` as the argument.
```js
({ test: () => assert.match(code, /generate_password\s*\(\s*8\s*\)/) })
```
You should assign `generate_password(8)` to a `new_password` variable.
```js
({
test: () => {
assert(__userGlobals.has("new_password"));
assert.match(code, /^new_password\s*=\s*generate_password\s*\(\s*8\s*\)/m);
}
})
```
# --seed--
## --seed-contents--
```py
import secrets
import string
def generate_password(length):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
return password
--fcc-editable-region--
--fcc-editable-region--
```
@@ -0,0 +1,49 @@
---
id: 656491fa4e69005287eb5a9a
title: Step 21
challengeType: 20
dashedName: step-21
---
# --description--
Check the result by printing your new variable.
# --hints--
You should print `new_password`.
```js
({ test: () => assert.match(code, /^print\s*\(\s*new_password\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
import secrets
import string
def generate_password(length):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
return password
--fcc-editable-region--
new_password = generate_password(8)
--fcc-editable-region--
```
@@ -0,0 +1,56 @@
---
id: 656494269ccce754411a2112
title: Step 27
challengeType: 20
dashedName: step-27
---
# --description--
The `re` module allows you to use *regular expressions* in your code. You will learn more about regular expressions very soon.
For now, add an `import` statement at the top of your code to import the `re` module.
# --hints--
You should import the `re` module.
```js
({ test: () => assert.match(code, /^import\s+re/m) })
```
# --seed--
## --seed-contents--
```py
--fcc-editable-region--
import secrets
import string
--fcc-editable-region--
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, '')
]
return password
# new_password = generate_password(8)
# print(new_password)
```
@@ -0,0 +1,71 @@
---
id: 65649a631440b757f9f7b428
title: Step 28
challengeType: 20
dashedName: step-28
---
# --description--
A regular expression, or regex, is a pattern used to match a specific combination of characters inside a string. It is a valid alternative to `if`/`else` conditional statements when you need to match or find patterns inside a string for validation purposes, character replacement, and others.
The `compile()` function from the `re` module compiles the string passed as the argument into a regular expression object that can be used by other `re` methods.
Declare a new `pattern` variable and assign the value of `re.compile('i')` to this variable.
# --hints--
You should declare a `pattern` variable.
```js
({ test: () => assert(__userGlobals.has("pattern")) })
```
You should assign `re.compile('i')` to your `pattern` variable.
```js
({ test: () => assert(__pyodide.runPython(`
import re
p = __locals.get("pattern")
p == re.compile('i')
`))
})
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, '')
]
return password
# new_password = generate_password(8)
# print(new_password)
--fcc-editable-region--
--fcc-editable-region--
```
@@ -0,0 +1,54 @@
---
id: 6564a767a7241362f7d8d664
title: Step 18
challengeType: 20
dashedName: step-18
---
# --description--
A standalone single underscore is used to represent a value you don't care or that won't be used in your code.
Your iteration variable is not actually used.
Modify your `i` variable into a single underscore.
# --hints--
You should modify your `i` variable into `_`.
```js
({ test: () =>
{
const transformedCode = code.replace(/\r/g, "");
const generate_pw = __helpers.python.getDef("\n"+transformedCode, "generate_password");
const {function_body} = generate_pw;
assert(function_body.match(/for\s+_\s+in\s+range\s*\(\s*length\s*\)\s*:/));
}
})
```
# --seed--
## --seed-contents--
```py
import secrets
import string
def generate_password(length):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
--fcc-editable-region--
password = ''
# Generate password
for i in range(length):
password += secrets.choice(all_characters)
--fcc-editable-region--
```
@@ -0,0 +1,71 @@
---
id: 6564b8c9349bd76dc037967b
title: Step 29
challengeType: 20
dashedName: step-29
---
# --description--
The `search()` function from the `re` module analyzes the string passed as the argument looking for the first place where the regex pattern matches the string.
Declare a variable called `quote` and assign the string `Not all those who wander are lost.` to this variable. Then, print the result of `pattern.search(quote)`.
# --hints--
You should have a `quote` variable.
```js
({ test: () => assert(__userGlobals.has("quote")) })
```
You should assign the provided string to your new `quote` variable.
```js
({ test: () => assert.equal(__userGlobals.get("quote"), "Not all those who wander are lost.") })
```
You should print `pattern.search(quote)`.
```js
({ test: () => assert.match(code, /^print\s*\(\s*pattern\.search\s*\(\s*quote\s*\)\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, '')
]
return password
# new_password = generate_password(8)
# print(new_password)
--fcc-editable-region--
pattern = re.compile('i')
--fcc-editable-region--
```
@@ -0,0 +1,67 @@
---
id: 6564c67db26c417561ab510d
title: Step 31
challengeType: 20
dashedName: step-31
---
# --description--
As you can see from the output, now your regex matches the first `l` inside the string.
In your pattern, you can add a quantifier after a character to specify how many times that character should be repeated. For example, the `+` quantifier means it should repeat one or more times.
Add a `+` quantifier to your pattern.
# --hints--
You should modify your `pattern` variable into `re.compile('l+')`.
```js
({ test: () => assert(__pyodide.runPython(`
import re
p = __locals.get("pattern")
p == re.compile('l+')
`))
})
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, '')
]
return password
# new_password = generate_password(8)
# print(new_password)
--fcc-editable-region--
pattern = re.compile('l')
quote = 'Not all those who wander are lost.'
print(pattern.search(quote))
--fcc-editable-region--
```
@@ -0,0 +1,60 @@
---
id: 6564cbb6311a40783b3f5de6
title: Step 36
challengeType: 20
dashedName: step-36
---
# --description--
Character classes also allow you to indicate a range of characters to match. You need to specify the starting and the ending characters separated by an hyphen, `-`. Characters follow the Unicode order.
Modify your `pattern` variable to match any letter `t` preceded by a lowercase letter in the `quote` variable. Use the range of characters from `a` to `z` for that.
# --hints--
You should modify your `pattern` variable to match any letter `t` in `quote` preceded by a lowercase letter using the `[a-z]` class.
```js
({ test: () => assert.equal(__userGlobals.get("pattern"), "[a-z]t") })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, '[0123456789]')
]
return password
# new_password = generate_password(8)
# print(new_password)
--fcc-editable-region--
pattern = 'w[ha]'
quote = 'Not all those who wander are lost.'
print(re.findall(pattern, quote))
--fcc-editable-region--
```
@@ -0,0 +1,58 @@
---
id: 6564cf2e8642517abdf9d6e2
title: Step 37
challengeType: 20
dashedName: step-37
---
# --description--
Now, modify the pattern in your constraint tuple to indicate the range of all digits using square brackets.
# --hints--
You should have the `(nums, '[0-9]')` tuple in your `constraints` list.
```js
({ test: () => assert.match(code, /constraints\s*=\s*\[\s*\(\s*nums\s*,\s*("|')\[0-9\]\1\s*\)\s*\]/) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
--fcc-editable-region--
constraints = [
(nums, '[0123456789]')
]
--fcc-editable-region--
return password
# new_password = generate_password(8)
# print(new_password)
pattern = '[a-z]t'
quote = 'Not all those who wander are lost.'
#print(re.findall(pattern, quote))
```
@@ -0,0 +1,60 @@
---
id: 6564d096a55d707bd77ab67b
title: Step 38
challengeType: 20
dashedName: step-38
---
# --description--
The caret, `^`, placed at the beginning of the character class, matches all the characters except those specified in the class.
Add a `^` as the first character inside your character class and see what happens.
# --hints--
You should add a `^` as the first character inside your character class.
```js
({ test: () => assert.equal(__userGlobals.get("pattern"), "[^a-z]t") })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, '[0-9]')
]
return password
# new_password = generate_password(8)
# print(new_password)
--fcc-editable-region--
pattern = '[a-z]t'
quote = 'Not all those who wander are lost.'
print(re.findall(pattern, quote))
--fcc-editable-region--
```
@@ -0,0 +1,58 @@
---
id: 6564d27c8e4c197d367c3b1c
title: Step 39
challengeType: 20
dashedName: step-39
---
# --description--
Add a new tuple to the `constraints` list. Use `lowercase` as the first item and a regex pattern that matches a single lowercase letter as the second item.
# --hints--
You should add a second tuple to the `constraints` list. Use the `lowercase` parameter as the first item and `[a-z]` as the second item.
```js
({ test: () => assert.match(code, /constraints\s*=\s*\[\s*\(\s*nums\s*,\s*("|')\[0-9\]\1\s*\)\s*,\s*\(\s*lowercase\s*,\s*("|')\[a-z\]\2\s*\)\s*,?\s*\]/) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
--fcc-editable-region--
constraints = [
(nums, '[0-9]')
]
--fcc-editable-region--
return password
# new_password = generate_password(8)
# print(new_password)
pattern = '[^a-z]t'
quote = 'Not all those who wander are lost.'
# print(re.findall(pattern, quote))
```
@@ -0,0 +1,59 @@
---
id: 6564d2eeb36ebe7dd9bd1ee9
title: Step 40
challengeType: 20
dashedName: step-40
---
# --description--
Add a third tuple to the `constraints` list. Use the `uppercase` parameter as the first item and a regex pattern that matches a single uppercase letter as the second item.
# --hints--
You should add a third tuple to the `constraints` list using `uppercase` as the first item and a character class that matches a single uppercase letter as the second item.
```js
({ test: () => assert.match(code, /constraints\s*=\s*\[\s*\(\s*nums\s*,\s*("|')\[0-9\]\1\s*\)\s*,\s*\(\s*lowercase\s*,\s*("|')\[a-z\]\2\s*\)\s*,\s*\(\s*uppercase\s*,\s*("|')\[A-Z\]\3\s*\)\s*,?\s*\]/) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
--fcc-editable-region--
constraints = [
(nums, '[0-9]'),
(lowercase, '[a-z]')
]
--fcc-editable-region--
return password
# new_password = generate_password(8)
# print(new_password)
pattern = '[^a-z]t'
quote = 'Not all those who wander are lost.'
# print(re.findall(pattern, quote))
```
@@ -0,0 +1,60 @@
---
id: 6564d500f1a48e7f2b732a37
title: Step 41
challengeType: 20
dashedName: step-41
---
# --description--
Add one last tuple to your list. Use the `special_chars` parameter as the first item and an empty string as the second item.
# --hints--
You should add a fourth tuple to the `constraints` list using `special_chars` as the first item and an empty string as the second item.
```js
({ test: () => assert.match(code, /constraints\s*=\s*\[\s*\(\s*nums\s*,\s*("|')\[0-9\]\1\s*\)\s*,\s*\(\s*lowercase\s*,\s*("|')\[a-z\]\2\s*\)\s*,\s*\(\s*uppercase\s*,\s*("|')\[A-Z\]\3\s*\)\s*,\s*\(\s*special_chars\s*,\s*("|')\4\s*\)\s*,?\s*\]/) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
--fcc-editable-region--
constraints = [
(nums, '[0-9]'),
(lowercase, '[a-z]'),
(uppercase, '[A-Z]')
]
--fcc-editable-region--
return password
# new_password = generate_password(8)
# print(new_password)
pattern = '[^a-z]t'
quote = 'Not all those who wander are lost.'
# print(re.findall(pattern, quote))
```
@@ -0,0 +1,61 @@
---
id: 6564d68c34027a8072a704f4
title: Step 42
challengeType: 20
dashedName: step-42
---
# --description--
The dot character is a wildcard that matches any character in a string — except for a newline character by default. Modify `pattern` to match the entire string. Use a `.` followed by the `+` quantifier.
# --hints--
You should modify your `pattern` variable to match the whole `quote` string.
```js
({ test: () => assert.equal(__userGlobals.get("pattern"), ".+") })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, '[0-9]'),
(lowercase, '[a-z]'),
(uppercase, '[A-Z]'),
(special_chars, '')
]
return password
# new_password = generate_password(8)
# print(new_password)
--fcc-editable-region--
pattern = '[^a-z]t'
quote = 'Not all those who wander are lost.'
print(re.findall(pattern, quote))
--fcc-editable-region--
```
@@ -0,0 +1,63 @@
---
id: 6564d75a923d21815caaa445
title: Step 43
challengeType: 20
dashedName: step-43
---
# --description--
If you need to match a character that has a special meaning in the pattern, such as `.` or `+`, you can escape it by prepending a backslash character, `\`. For example, this matches a literal plus sign: `\+`.
Modify `pattern` so that it matches a single literal dot.
# --hints--
You should modify your `pattern` variable into `\.`.
```js
({ test: () => assert.match(code, /^pattern\s*=\s*("|')\\\.\1/m) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, '[0-9]'),
(lowercase, '[a-z]'),
(uppercase, '[A-Z]'),
(special_chars, '')
]
return password
# new_password = generate_password(8)
# print(new_password)
--fcc-editable-region--
pattern = '.+'
quote = 'Not all those who wander are lost.'
print(re.findall(pattern, quote))
--fcc-editable-region--
```
@@ -0,0 +1,63 @@
---
id: 6564d97a5ef99783216a6229
title: Step 44
challengeType: 20
dashedName: step-44
---
# --description--
Python provides a particular type of string called *raw* string. Raw strings are prefixed with a `r`. The key distinction from regular strings lies in how they handle the backslash character: in raw strings, backslashes are treated as literal characters rather than escape characters. When writing regular expressions, using raw strings is a good practice, since they can usually contain a lot of `\` characters.
Turn your `pattern` string into a raw string by prefixing it with a `r`.
# --hints--
You should modify your `pattern` variable into `r'\.'`.
```js
({ test: () => assert.match(code, /^pattern\s*=\s*r("|')\\\.\1/m) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, '[0-9]'),
(lowercase, '[a-z]'),
(uppercase, '[A-Z]'),
(special_chars, '')
]
return password
# new_password = generate_password(8)
# print(new_password)
--fcc-editable-region--
pattern = '\.'
quote = 'Not all those who wander are lost.'
print(re.findall(pattern, quote))
--fcc-editable-region--
```
@@ -0,0 +1,61 @@
---
id: 6564daa2725b9b8420141b1c
title: Step 45
challengeType: 20
dashedName: step-45
---
# --description--
Now, turn the four patterns from the `constraints` list into raw strings.
# --hints--
You should turn the four strings inside the `constraints` list into raw strings.
```js
({ test: () => assert.match(code, /constraints\s*=\s*\[\s*\(\s*nums\s*,\s*r("|')\[0-9\]\1\s*\)\s*,\s*\(\s*lowercase\s*,\s*r("|')\[a-z\]\2\s*\)\s*,\s*\(\s*uppercase\s*,\s*r("|')\[A-Z\]\3\s*\)\s*,\s*\(\s*special_chars\s*,\s*r("|')\4\s*\)\s*,?\s*\]/) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
--fcc-editable-region--
constraints = [
(nums, '[0-9]'),
(lowercase, '[a-z]'),
(uppercase, '[A-Z]'),
(special_chars, '')
]
--fcc-editable-region--
return password
# new_password = generate_password(8)
# print(new_password)
pattern = r'\.'
quote = 'Not all those who wander are lost.'
# print(re.findall(pattern, quote))
```
@@ -0,0 +1,61 @@
---
id: 6564db880cf9408535f17ff4
title: Step 46
challengeType: 20
dashedName: step-46
---
# --description--
The character class `\d` is a shorthand for `[0-9]`. Replace this character class with the shorthand inside your first constraint tuple.
# --hints--
You should turn the `[0-9]` class into `\d`.
```js
({ test: () => assert.match(code, /constraints\s*=\s*\[\s*\(\s*nums\s*,\s*r("|')\\d\1\s*\)\s*,\s*\(\s*lowercase\s*,\s*r("|')\[a-z\]\2\s*\)\s*,\s*\(\s*uppercase\s*,\s*r("|')\[A-Z\]\3\s*\)\s*,\s*\(\s*special_chars\s*,\s*r("|')\4\s*\)\s*,?\s*\]/) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
--fcc-editable-region--
constraints = [
(nums, r'[0-9]'),
(lowercase, r'[a-z]'),
(uppercase, r'[A-Z]'),
(special_chars, r'')
]
--fcc-editable-region--
return password
# new_password = generate_password(8)
# print(new_password)
pattern = r'\.'
quote = 'Not all those who wander are lost.'
# print(re.findall(pattern, quote))
```
@@ -0,0 +1,63 @@
---
id: 6564dd65c3c2fa873a83d213
title: Step 47
challengeType: 20
dashedName: step-47
---
# --description--
In a character class, you can combine multiple ranges by writing one range after another inside the square brackets (without any additional characters). For example: `[a-d3-6]` is the combination of `[a-d]` and `[3-6]`.
Now, create your fourth pattern to match any non-alphanumeric character. Combine the `a-z`, `A-Z`, and `0-9` ranges into a single character class and add a `^` as the first character to negate the pattern.
# --hints--
You should complete your fourth pattern to match any non-alphanumeric character.
```js
({ test: () => assert.match(code, /constraints\s*=\s*\[\s*\(\s*nums\s*,\s*r("|')\\d\1\s*\)\s*,\s*\(\s*lowercase\s*,\s*r("|')\[a-z\]\2\s*\)\s*,\s*\(\s*uppercase\s*,\s*r("|')\[A-Z\]\3\s*\)\s*,\s*\(\s*special_chars\s*,\s*r("|')\[\^a-zA-Z0-9\]\4\s*\)\s*,?\s*\]/) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
--fcc-editable-region--
constraints = [
(nums, r'\d'),
(lowercase, r'[a-z]'),
(uppercase, r'[A-Z]'),
(special_chars, r'')
]
--fcc-editable-region--
return password
# new_password = generate_password(8)
# print(new_password)
pattern = r'\.'
quote = 'Not all those who wander are lost.'
# print(re.findall(pattern, quote))
```
@@ -0,0 +1,63 @@
---
id: 6564de10a0887f882b0012d3
title: Step 48
challengeType: 20
dashedName: step-48
---
# --description--
In the same way the `[0-9]` class is equivalent to `\d`, the `[^0-9]` class is equivalent to `\D`. Alphanumeric characters can be matched with `\w` and non-alphanumeric characters can be matched with `\W`.
Replace the `[^a-zA-Z0-9]` character class with `\W`.
# --hints--
You should replace the `[^a-zA-Z0-9]` character class with `\W`.
```js
({ test: () => assert.match(code, /constraints\s*=\s*\[\s*\(\s*nums\s*,\s*r("|')\\d\1\s*\)\s*,\s*\(\s*lowercase\s*,\s*r("|')\[a-z\]\2\s*\)\s*,\s*\(\s*uppercase\s*,\s*r("|')\[A-Z\]\3\s*\)\s*,\s*\(\s*special_chars\s*,\s*r("|')\\W\4\s*\)\s*,?\s*\]/) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
--fcc-editable-region--
constraints = [
(nums, r'\d'),
(lowercase, r'[a-z]'),
(uppercase, r'[A-Z]'),
(special_chars, r'[^a-zA-Z0-9]')
]
--fcc-editable-region--
return password
# new_password = generate_password(8)
# print(new_password)
pattern = r'\.'
quote = 'Not all those who wander are lost.'
# print(re.findall(pattern, quote))
```
@@ -0,0 +1,61 @@
---
id: 6564eebf0d2d6390b9377197
title: Step 49
challengeType: 20
dashedName: step-49
---
# --description--
Now, turn `pattern` into the shorthand class for non-alphanumeric characters.
# --hints--
Your `pattern` variable should be `\W`.
```js
({ test: () => assert.match(code, /^pattern\s*=\s*r("|')\\W\1/m) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(lowercase, r'[a-z]'),
(uppercase, r'[A-Z]'),
(special_chars, r'\W')
]
return password
# new_password = generate_password(8)
# print(new_password)
--fcc-editable-region--
pattern = r'\.'
quote = 'Not all those who wander are lost.'
print(re.findall(pattern, quote))
--fcc-editable-region--
```
@@ -0,0 +1,63 @@
---
id: 6564efa70114b591b74d5679
title: Step 50
challengeType: 20
dashedName: step-50
---
# --description--
The space characters and the final period are matched, as they are the only non-alphanumeric characters in the string.
Now turn your `quote` string into a single underscore character.
# --hints--
Your `quote` variable should be `_`.
```js
({ test: () => assert.equal(__userGlobals.get("quote"), "_") })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(lowercase, r'[a-z]'),
(uppercase, r'[A-Z]'),
(special_chars, r'\W')
]
return password
# new_password = generate_password(8)
# print(new_password)
--fcc-editable-region--
pattern = r'\W'
quote = 'Not all those who wander are lost.'
print(re.findall(pattern, quote))
--fcc-editable-region--
```
@@ -0,0 +1,69 @@
---
id: 6564f0279e23ce924eedd1b2
title: Step 51
challengeType: 20
dashedName: step-51
---
# --description--
Since the underscore character is a valid character for variable names, it is included in the `\w` character class (equivalent to `[a-zA-Z0-9_]`), which can be conveniently used to match variable names.
Therefore, the `\W` character class is equivalent to `[^a-zA-Z0-9_]` with the underscore character that is not matched. For this reason you cannot use it to match all your special characters.
Delete the last three lines in your code.
# --hints--
You should delete the last three lines in your code.
```js
({ test: () => {
assert.isFalse(/pattern\s*=\s*r("|')\\W\1/.test(code));
assert.isFalse(/quote\s*=\s*("|')_\1/.test(code));
assert.isFalse(/print\(re\.findall\(pattern, quote\)\)/.test(code));
}})
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(lowercase, r'[a-z]'),
(uppercase, r'[A-Z]'),
(special_chars, r'\W')
]
return password
# new_password = generate_password(8)
# print(new_password)
--fcc-editable-region--
pattern = r'\W'
quote = '_'
print(re.findall(pattern, quote))
--fcc-editable-region--
```
@@ -0,0 +1,58 @@
---
id: 6564f32b18480893cf7799fd
title: Step 52
challengeType: 20
dashedName: step-52
---
# --description--
Now, combine your raw string with an f-string and interpolate your `symbols` variable inside the character class. Remember that you can interpolate a variable within an f-string using curly brackets `{ }`.
# --hints--
The second item in your fourth constraint tuple should be the string `fr'[{symbols}]'`.
```js
({ test: () => assert.match(code, /\(\s*special_chars\s*,\s*fr("|')\[\{\s*symbols\s*\}\]\1/) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(lowercase, r'[a-z]'),
(uppercase, r'[A-Z]'),
--fcc-editable-region--
(special_chars, r'\W')
--fcc-editable-region--
]
return password
# new_password = generate_password(8)
# print(new_password)
```
@@ -0,0 +1,57 @@
---
id: 6564f583ca6fbf9556098dd6
title: Step 53
challengeType: 20
dashedName: step-53
---
# --description--
Below the `constraints` list, add a comment saying `Check constraints`.
# --hints--
You should add the provided comment.
```js
({ test: () => assert.match(code, /#\s*Check\sconstraints/) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
--fcc-editable-region--
constraints = [
(nums, r'\d'),
(lowercase, r'[a-z]'),
(uppercase, r'[A-Z]'),
(special_chars, fr'[{symbols}]')
]
--fcc-editable-region--
return password
# new_password = generate_password(8)
# print(new_password)
```
@@ -0,0 +1,59 @@
---
id: 6564f834dd717998092cfd47
title: Step 54
challengeType: 20
dashedName: step-54
---
# --description--
After your new comment, write a `for` loop to iterate over the `constraints` list. Use `constraint` and `pattern` as the iterating variables.
# --hints--
You should write a `for` loop to iterate over the `constraints` list with `constraint` and `pattern`.
```js
assert.match(code, /for\s+constraint\s*,\s*pattern\s+in\s+constraints\s*:/)
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(lowercase, r'[a-z]'),
(uppercase, r'[A-Z]'),
(special_chars, fr'[{symbols}]')
]
--fcc-editable-region--
# Check constraints
--fcc-editable-region--
return password
# new_password = generate_password(8)
# print(new_password)
```
@@ -0,0 +1,59 @@
---
id: 6564fd78ffff909b1531d3c3
title: Step 55
challengeType: 20
dashedName: step-55
---
# --description--
Inside the `for` loop, call the `findall()` function passing `pattern` and `password` as the arguments.
# --hints--
You should call `re.findall()` passing `pattern` and `password` as the arguments.
```js
({ test: () => assert.match(code, /^(\s*)for.+:\s*^\1\s{4}re\.findall\s*\(\s*pattern\s*,\s*password\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(lowercase, r'[a-z]'),
(uppercase, r'[A-Z]'),
(special_chars, fr'[{symbols}]')
]
--fcc-editable-region--
# Check constraints
for constraint, pattern in constraints:
--fcc-editable-region--
return password
# new_password = generate_password(8)
# print(new_password)
```
@@ -0,0 +1,62 @@
---
id: 6565012a2564509d40a90048
title: Step 56
challengeType: 20
dashedName: step-56
---
# --description--
You are interested in the number of elements in the list returned by the `findall()` function.
Pass your existent `findall()` call to the `len()` function.
# --hints--
You should call the `len()` function with `re.findall(pattern, password)` as the argument.
```js
({ test: () => assert.match(code, /^(\s*)for.+:\s*^\1\s{4}len\s*\(\s*re\.findall\s*\(\s*pattern\s*,\s*password\s*\)\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(lowercase, r'[a-z]'),
(uppercase, r'[A-Z]'),
(special_chars, fr'[{symbols}]')
]
--fcc-editable-region--
# Check constraints
for constraint, pattern in constraints:
re.findall(pattern, password)
--fcc-editable-region--
return password
# new_password = generate_password(8)
# print(new_password)
```
@@ -0,0 +1,60 @@
---
id: 6565bb128adfcd5ec362382d
title: Step 57
challengeType: 20
dashedName: step-57
---
# --description--
Inside your `for` loop, compare `constraint` and the length of the list returned by `findall()`. Use the `<=` operator for that.
# --hints--
You should compare `constraint` and the length of the list returned by `findall` using the `<=` operator inside your `for` loop.
```js
({ test: () => assert.match(code, /^(\s*)for.+:\s*^\1\s{4}constraint\s*<=\s*len\s*\(\s*re\.findall\s*\(\s*pattern\s*,\s*password\s*\)\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(lowercase, r'[a-z]'),
(uppercase, r'[A-Z]'),
(special_chars, fr'[{symbols}]')
]
--fcc-editable-region--
# Check constraints
for constraint, pattern in constraints:
len(re.findall(pattern, password))
--fcc-editable-region--
return password
# new_password = generate_password(8)
# print(new_password)
```
@@ -0,0 +1,67 @@
---
id: 6565bd4265158360de8e2ae7
title: Step 59
challengeType: 20
dashedName: step-59
---
# --description--
Turn the expression inside your `for` loop into an `if` statement. Use the expression you wrote in the previous step as the `if` condition. Inside the new conditional statement, increment the `count` value by `1`.
# --hints--
You should turn `constraint <= len(re.findall(pattern, password))` into the `if` condition.
```js
({ test: () => assert.match(code, /^(\s*)for.+:\s*^\1(\s{4})if\s+constraint\s* <=\s*len\s*\(\s*re\.findall\s*\(\s*pattern\s*,\s*password\s*\)\s*\)\s*:/m) })
```
You should increment `count` by one inside your new `if` statement.
```js
({ test: () => assert.match(code, /^(\s*)for.+:\s*^\1(\s{4})if\s+constraint\s* <=\s*len\s*\(\s*re\.findall\s*\(\s*pattern\s*,\s*password\s*\)\s*\)\s*:\s*^\1\2\2(count\s*\+=\s*1|count\s*=\s*count\s*\+\s*1)/m) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(lowercase, r'[a-z]'),
(uppercase, r'[A-Z]'),
(special_chars, fr'[{symbols}]')
]
--fcc-editable-region--
# Check constraints
count = 0
for constraint, pattern in constraints:
constraint <= len(re.findall(pattern, password))
--fcc-editable-region--
return password
# new_password = generate_password(8)
# print(new_password)
```
@@ -0,0 +1,61 @@
---
id: 6565c014db6e9b63c257771d
title: Step 58
challengeType: 20
dashedName: step-58
---
# --description--
Right before your `for` loop, declare a `count` variable and assign the value zero to this variable.
# --hints--
You should declare a `count` variable and assign the value `0` to this variable.
```js
({ test: () => assert.match(code, /count\s*=\s*0/) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(lowercase, r'[a-z]'),
(uppercase, r'[A-Z]'),
(special_chars, fr'[{symbols}]')
]
--fcc-editable-region--
# Check constraints
for constraint, pattern in constraints:
constraint <= len(re.findall(pattern, password))
--fcc-editable-region--
return password
# new_password = generate_password(8)
# print(new_password)
```
@@ -0,0 +1,68 @@
---
id: 6565c084627071646f94c4b0
title: Step 60
challengeType: 20
dashedName: step-60
---
# --description--
Finally, after the `for` loop, create an `if` statement to check if `count` is equal to `4` and break out of the `while` loop by using the `break` statement.
# --hints--
You should create an `if` statement that checks if `count` is equal to `4` after the `for` loop.
```js
({ test: () => assert.match(code, /^(\s*)for.+:\s*^\1(\s{4})if\s+constraint\s* <=\s*len\s*\(\s*re\.findall\s*\(\s*pattern\s*,\s*password\s*\)\s*\)\s*:\s*^\1\2\2count\s*\+=\s*1\s*^\1if\s+count\s*==\s*4\s*:/m) })
```
You should use `break` inside your new `if` to break out of the `while` loop.
```js
({ test: () => assert.match(code, /^(\s*)for.+:\s*^\1(\s{4})if\s+constraint\s* <=\s*len\s*\(\s*re\.findall\s*\(\s*pattern\s*,\s*password\s*\)\s*\)\s*:\s*^\1\2\2count\s*\+=\s*1\s*^\1if\s+count\s*==\s*4\s*:\s*^\1\2break/m) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(lowercase, r'[a-z]'),
(uppercase, r'[A-Z]'),
(special_chars, fr'[{symbols}]')
]
--fcc-editable-region--
# Check constraints
count = 0
for constraint, pattern in constraints:
if constraint <= len(re.findall(pattern, password)):
count += 1
--fcc-editable-region--
return password
# new_password = generate_password(8)
# print(new_password)
```
@@ -0,0 +1,71 @@
---
id: 6565c13fdb798865c161d8f8
title: Step 65
challengeType: 20
dashedName: step-65
---
# --description--
Now it's time to test your function. Uncomment the last two lines in your code and modify the function call passing 5 arguments. Use `8` for the length and `1` for the other four constraints.
# --hints--
You should call `generate_password` with the provided arguments.
```js
({ test: () => assert.match(code, /^new_password\s*=\s*generate_password\s*\(\s*8\s*,\s*1\s*,\s*1\s*,\s*1\s*,\s*1\s*\)/m) })
```
You should print your `new_password` variable.
```js
({ test: () => assert.match(code, /^print\s*\(\s*new_password\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(lowercase, r'[a-z]'),
(uppercase, r'[A-Z]'),
(special_chars, fr'[{symbols}]')
]
# Check constraints
if all(
constraint <= len(re.findall(pattern, password))
for constraint, pattern in constraints
):
break
return password
--fcc-editable-region--
# new_password = generate_password(8)
# print(new_password)
--fcc-editable-region--
```
@@ -0,0 +1,67 @@
---
id: 6565c234de8cdf673c96bdf3
title: Step 67
challengeType: 20
dashedName: step-67
---
# --description--
As long as all the arguments in a function call are keyword arguments, the order of the arguments doesn't matter.
To confirm this, try to change the order of `length=8` and `nums=1` in your function call.
# --hints--
You should change the order of `length=8` and `nums=1` in your `generate_password()` call.
```js
({ test: () => assert.match(code, /^new_password\s*=\s*generate_password\s*\(\s*nums\s*=\s*1\s*,\s*length\s*=\s*8\s*,\s*special_chars\s*=\s*1\s*,\s*uppercase\s*=\s*1\s*,\s*lowercase\s*=\s*1\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(special_chars, fr'[{symbols}]'),
(uppercase, r'[A-Z]'),
(lowercase, r'[a-z]')
]
# Check constraints
if all(
constraint <= len(re.findall(pattern, password))
for constraint, pattern in constraints
):
break
return password
--fcc-editable-region--
new_password = generate_password(length=8, nums=1, special_chars=1, uppercase=1, lowercase=1)
print(new_password)
--fcc-editable-region--
```
@@ -0,0 +1,66 @@
---
id: 6565c32f89ab8d68b42aff30
title: Step 70
challengeType: 20
dashedName: step-70
---
# --description--
Now, remove all the arguments from your function call.
# --hints--
You should call `generate_password` without arguments.
```js
({ test: () => assert.match(code, /^new_password\s*=\s*generate_password\s*\(\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length=16, nums=1, special_chars=1, uppercase=1, lowercase=1):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(special_chars, fr'[{symbols}]'),
(uppercase, r'[A-Z]'),
(lowercase, r'[a-z]')
]
# Check constraints
if all(
constraint <= len(re.findall(pattern, password))
for constraint, pattern in constraints
):
break
return password
--fcc-editable-region--
new_password = generate_password(length=8)
print(new_password)
--fcc-editable-region--
```
@@ -0,0 +1,66 @@
---
id: 6565c3a146bd5469b62bc59e
title: Step 71
challengeType: 20
dashedName: step-71
---
# --description--
Modify your `print()` call to take the string `Generated password:` as the first argument, before `new_password`.
# --hints--
You should pass the `Generated password:` string and `new_password` to your `print()` call.
```js
({ test: () => assert.match(code, /^print\s*\(\s*("|')Generated\spassword:\1\s*,\s*new_password\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length=16, nums=1, special_chars=1, uppercase=1, lowercase=1):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(special_chars, fr'[{symbols}]'),
(uppercase, r'[A-Z]'),
(lowercase, r'[a-z]')
]
# Check constraints
if all(
constraint <= len(re.findall(pattern, password))
for constraint, pattern in constraints
):
break
return password
--fcc-editable-region--
new_password = generate_password()
print(new_password)
--fcc-editable-region--
```
@@ -0,0 +1,116 @@
---
id: 6565c4767f49286aec825c6d
title: Step 72
challengeType: 20
dashedName: step-72
---
# --description--
Finally, put the last two lines of your code inside an `if` statement that execute when `__name__ == '__main__'`. In this way, your code won't run when imported as a module. Otherwise, it will call `generate_password()` and print the generated password.
With that, the password generator project is complete.
# --hints--
You should have an `if` statement that checks if `__name__ == '__main__'` and put the last two lines of your code in it.
```js
({ test: () => assert.match(code, /^if\s+__name__\s*==\s*("|')__main__\1\s*:\s*^(\s{4})new_password\s*=\s*generate_password\s*\(\s*\)\s*^\2print\s*\(\s*("|')Generated\spassword:\3\s*,\s*new_password\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length=16, nums=1, special_chars=1, uppercase=1, lowercase=1):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(special_chars, fr'[{symbols}]'),
(uppercase, r'[A-Z]'),
(lowercase, r'[a-z]')
]
# Check constraints
if all(
constraint <= len(re.findall(pattern, password))
for constraint, pattern in constraints
):
break
return password
--fcc-editable-region--
new_password = generate_password()
print('Generated password:', new_password)
--fcc-editable-region--
```
# --solutions--
```py
import re
import secrets
import string
def generate_password(length=16, nums=1, special_chars=1, uppercase=1, lowercase=1):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(special_chars, fr'[{symbols}]'),
(uppercase, r'[A-Z]'),
(lowercase, r'[a-z]')
]
# Check constraints
if all(
constraint <= len(re.findall(pattern, password))
for constraint, pattern in constraints
):
break
return password
if __name__ == '__main__':
new_password = generate_password()
print('Generated password:', new_password)
```
@@ -0,0 +1,65 @@
---
id: 656eec2f40d18056cc58b229
title: Step 30
challengeType: 20
dashedName: step-30
---
# --description--
The value `None` is returned since `i` is not found inside the parsed string.
Now, modify your pattern into `l` and see the result.
# --hints--
You should modify your `pattern` variable into `re.compile('l')`.
```js
({ test: () => assert(__pyodide.runPython(`
import re
p = __locals.get("pattern")
p == re.compile('l')
`))
})
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, '')
]
return password
# new_password = generate_password(8)
# print(new_password)
--fcc-editable-region--
pattern = re.compile('i')
quote = 'Not all those who wander are lost.'
print(pattern.search(quote))
--fcc-editable-region--
```
@@ -0,0 +1,55 @@
---
id: 656ef54070c72161e6feb90f
title: Step 22
challengeType: 20
dashedName: step-22
---
# --description--
It seems all fine, but it would be nice to have a way to check that the generated password complies to specific features. For example, a minimum number of special characters, digits, or uppercase/lowercase letters. You are going to take care of that very soon.
For now, comment out the last two lines of your code.
# --hints--
You should turn the last two lines of your code into comments.
```js
({ test: () => {
assert.match(code, /#\s*new_password\s*=\s*generate_password\s*\(\s*8\s*\)/);
assert.match(code, /#\s*print\s*\(\s*new_password\s*\)/);
} })
```
# --seed--
## --seed-contents--
```py
import secrets
import string
def generate_password(length):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
return password
--fcc-editable-region--
new_password = generate_password(8)
print(new_password)
--fcc-editable-region--
```
@@ -0,0 +1,56 @@
---
id: 656ef5bc5c0cd464be1df675
title: Step 23
challengeType: 20
dashedName: step-23
---
# --description--
Next, you are going to give your function more parameters that will act as constraints for the generated password.
Modify your function declaration by adding `nums`, `special_chars`, `uppercase`, and `lowercase` in this order after the existent `length` parameter.
# --hints--
Your function should take `length`, `nums`, `special_chars`, `uppercase`, and `lowercase` as the parameters. The order matters.
```js
({ test: () => assert(__pyodide.runPython(`
import inspect
foo = __locals.get("generate_password")
sig = str(inspect.signature(foo))
sig == '(length, nums, special_chars, uppercase, lowercase)'
`))
})
```
# --seed--
## --seed-contents--
```py
import secrets
import string
--fcc-editable-region--
def generate_password(length):
--fcc-editable-region--
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
return password
# new_password = generate_password(8)
# print(new_password)
```
@@ -0,0 +1,47 @@
---
id: 656ef783bba6976de014eaa8
title: Step 24
challengeType: 20
dashedName: step-24
---
# --description--
Put your `password` variable declaration and the following `for` loop inside a `while` loop. Use `True` as the condition for your new loop.
# --hints--
You should create a `while True` loop enclosing your existing `password` declaration and `for` loop.
```js
({ test: () => assert.match(code, /^(\s*)while\s+True\s*:\s*^\1\1password\s*=\s*("|')\2\s*#\s*Generate\spassword\s*^\1\1for\s+_\s+in\s+range\s*\(\s*length\s*\s*\)\s*:\s*^\1\1\1password\s*\+=\s*secrets\.choice\s*\(\s*all_characters\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
--fcc-editable-region--
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
--fcc-editable-region--
return password
# new_password = generate_password(8)
# print(new_password)
```
@@ -0,0 +1,56 @@
---
id: 656ef7f792734072dedd8319
title: Step 25
challengeType: 20
dashedName: step-25
---
# --description--
After your `for` loop, create a `constraints` variable and assign an empty list to this variable.
# --hints--
You should declare a `constraints` variable after your `for` loop and assign an empty list to this variable.
```js
({ test: () =>
{
const transformedCode = code.replace(/\r/g, "");
const generate_pw = __helpers.python.getDef("\n"+transformedCode, "generate_password");
const {function_body} = generate_pw;
assert(function_body.match(/^(\s*)for.*:.*^\1constraints\s*=\s*\[\s*\]/ms));
}
})
```
# --seed--
## --seed-contents--
```py
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
--fcc-editable-region--
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
--fcc-editable-region--
return password
# new_password = generate_password(8)
# print(new_password)
```
@@ -0,0 +1,65 @@
---
id: 656ef89b4b486b7a16077864
title: Step 26
challengeType: 20
dashedName: step-26
---
# --description--
A tuple is another built-in data structure in Python. Tuples are very much like lists, but they are defined with parentheses `()`, instead of square brackets. Also, tuples are immutable, unlike lists.
```py
my_tuple = ('larch', 1, True)
```
Your `constraints` list is going to store tuples. The first item of each tuple will be a constraint parameter.
Add a tuple to your list. Use `nums` as the first item and an empty string as the second item.
# --hints--
You should add a tuple to the `constraints` list.
```js
({ test: () => assert.match(code, /constraints\s*=\s*\[\s*\(.*\)\s*\]/) })
```
You should add `(nums, '')` to the `constraints` list.
```js
({ test: () => assert.match(code, /constraints\s*=\s*\[\s*\(\s*nums\s*,\s*("|')\1\s*\)\s*\]/) })
```
# --seed--
## --seed-contents--
```py
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
--fcc-editable-region--
constraints = []
--fcc-editable-region--
return password
# new_password = generate_password(8)
# print(new_password)
```
@@ -0,0 +1,64 @@
---
id: 656f04b20397d9a574cc7eb2
title: Step 32
challengeType: 20
dashedName: step-32
---
# --description--
You can obtain the same result without using the `compile()` function. Modify your `pattern` variable into the literal string `l+`. Then, change the `print()` call to print `re.search(pattern, quote)`.
# --hints--
You should modify your `pattern` variable into the literal string `l+`.
```js
({ test: () => assert.equal(__userGlobals.get("pattern"), "l+") })
```
You should print `re.search(pattern, quote)`.
```js
({ test: () => assert.match(code, /^print\s*\(\s*re\.search\s*\(\s*pattern\s*,\s*quote\s*\)\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, '')
]
return password
# new_password = generate_password(8)
# print(new_password)
--fcc-editable-region--
pattern = re.compile('l+')
quote = 'Not all those who wander are lost.'
print(pattern.search(quote))
--fcc-editable-region--
```
@@ -0,0 +1,60 @@
---
id: 656f0bc259c1f6b57486ed68
title: Step 33
challengeType: 20
dashedName: step-33
---
# --description--
To check that the generated password meets the required features, you are going to use the `findall()` function from the `re` module. It's similar to `search` but it returns a list with all the occurrences of the matched pattern.
Replace the `search()` call with `findall()` and check the output.
# --hints--
You should modify your existing `print()` call replacing `search()` with `findall()`.
```js
({ test: () => assert.match(code, /^print\s*\(\s*re\.findall\s*\(\s*pattern\s*,\s*quote\s*\)\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, '')
]
return password
# new_password = generate_password(8)
# print(new_password)
--fcc-editable-region--
pattern = 'l+'
quote = 'Not all those who wander are lost.'
print(re.search(pattern, quote))
--fcc-editable-region--
```
@@ -0,0 +1,64 @@
---
id: 656f0d6bc60b58b9777a36c9
title: Step 34
challengeType: 20
dashedName: step-34
---
# --description--
A character class is indicated by square brackets and matches one character among those specified between the brackets. For example, `ma[dnt]` matches either `mad`, `man`, or `mat`.
Modify your pattern to match a `w` followed by either `h` or `a`.
# --hints--
Your pattern should use a character class to match a `w` followed by either `h` or `a`.
```js
({ test: () => assert(__pyodide.runPython(`
regex = __locals.get("pattern")
regex == "w[ha]" or regex == "w[ah]"
`))
})
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, '')
]
return password
# new_password = generate_password(8)
# print(new_password)
--fcc-editable-region--
pattern = 'l+'
quote = 'Not all those who wander are lost.'
print(re.findall(pattern, quote))
--fcc-editable-region--
```
@@ -0,0 +1,58 @@
---
id: 656f10458134d4c4e283a2f1
title: Step 35
challengeType: 20
dashedName: step-35
---
# --description--
Now, turn the empty string in the constraint tuple into a regex pattern to match a single digit. Use a character class specifying each digit from `0` to `9`.
# --hints--
You should have the `(nums, '[0123456789]')` tuple in your `constraints` list.
```js
({ test: () => assert.match(code, /constraints\s*=\s*\[\s*\(\s*nums\s*,\s*("|')\[\d{10}\]\1\s*\)\s*\]/) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
--fcc-editable-region--
constraints = [
(nums, '')
]
--fcc-editable-region--
return password
# new_password = generate_password(8)
# print(new_password)
pattern = 'w[ha]'
quote = 'Not all those who wander are lost.'
# print(re.findall(pattern, quote))
```
@@ -0,0 +1,80 @@
---
id: 6577319039f4f7de9251b822
title: Step 61
challengeType: 20
dashedName: step-61
---
# --description--
Instead of using a loop and a counter variable, you can achieve the same result with a different approach.
Replace your existing `for` loop and two `if` statements with a single `if` statement. For the `if` condition, use a list created with the list comprehension syntax.
The list stores the results of evaluating the expression `constraint <= len(re.findall(pattern, password))` for each `constraint`-`pattern` tuple in the `constraints` list.
# --hints--
You should replace your existing `for` loop and two `if` statements with a single `if` statement.
```js
({ test: () => {
assert.match(code, /^(\s{8})if\s+.+:\s*^\1\s{4}break/ms);
assert.isFalse(/if\s+count\s*==\s*4\s*:/.test(code));
} })
```
Your new `if` condition should be a list comprehension that is the result of evaluating the expression `constraint <= len(re.findall(pattern, password))` for each `constraint`-`pattern` tuple in the `constraints` list.
```js
({ test: () => assert.match(code, /^(\s{8})if\s+\[\s*constraint\s*<=\s*len\s*\(\s*re\.findall\s*\(\s*pattern\s*,\s*password\s*\)\s*\)\s+for\s+constraint\s*,\s*pattern\s+in\s+constraints\s*\]\s*:\s*^\1\s{4}break/m) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(lowercase, r'[a-z]'),
(uppercase, r'[A-Z]'),
(special_chars, fr'[{symbols}]')
]
# Check constraints
count = 0
--fcc-editable-region--
for constraint, pattern in constraints:
if constraint <= len(re.findall(pattern, password)):
count += 1
if count == 4:
--fcc-editable-region--
break
return password
# new_password = generate_password(8)
# print(new_password)
```
@@ -0,0 +1,68 @@
---
id: 657732654845d8e2fb1217e6
title: Step 62
challengeType: 20
dashedName: step-62
---
# --description--
`all()` is a built-in Python function that returns `True` if all the elements inside a given iterable evaluate to `True`. Otherwise, it returns `False`.
You can combine the `all()` function with the list comphehension syntax to make your code more concise.
Modify your `if` condition by passing the list comprehension you created in the previous step to the `all()` function.
# --hints--
You should pass your list comprehension to the `all()` function.
```js
({ test: () => assert.match(code, /^(\s*)if\s+all\s*\(\s*\[\s*constraint\s*<=\s*len\s*\(\s*re\.findall\s*\(\s*pattern\s*,\s*password\s*\)\s*\)\s*for\s+constraint\s*,\s*pattern\s+in\s+constraints\s*\]\s*\)\s*:\s*^\1\s{4}break/m) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(lowercase, r'[a-z]'),
(uppercase, r'[A-Z]'),
(special_chars, fr'[{symbols}]')
]
# Check constraints
count = 0
--fcc-editable-region--
if [constraint <= len(re.findall(pattern, password))
for constraint, pattern in constraints]:
--fcc-editable-region--
break
return password
# new_password = generate_password(8)
# print(new_password)
```
@@ -0,0 +1,72 @@
---
id: 6577333feab1e8e927014f03
title: Step 63
challengeType: 20
dashedName: step-63
---
# --description--
Having `all([expression for i in iterable])`, means that a new list is created by evaluating `expression` for each `i` in `iterable`. After the `all()` function iterates over the newly created list, the list is deleted automatically, since it is no longer needed.
Memory can be saved by using a generator expression. Generator expressions follow the syntax of list comprehensions but they use parentheses instead of square brackets.
Change your list comprehension into a generator expression by removing the square brackets.
# --hints--
You should turn your list comprehension into a generator expression by removing the square brackets.
```js
({ test: () => assert.match(code, /^(\s*)if\s+all\s*\(\s*constraint\s*<=\s*len\s*\(\s*re\.findall\s*\(\s*pattern\s*,\s*password\s*\)\s*\)\s*for\s+constraint\s*,\s*pattern\s+in\s+constraints\s*\)\s*:\s*^\1\s{4}break/m) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(lowercase, r'[a-z]'),
(uppercase, r'[A-Z]'),
(special_chars, fr'[{symbols}]')
]
# Check constraints
count = 0
--fcc-editable-region--
if all(
[
constraint <= len(re.findall(pattern, password))
for constraint, pattern in constraints
]
):
--fcc-editable-region--
break
return password
# new_password = generate_password(8)
# print(new_password)
```
@@ -0,0 +1,65 @@
---
id: 657733bc97eb83efdb7e3988
title: Step 64
challengeType: 20
dashedName: step-64
---
# --description--
You don't need the `count` variable anymore. Delete this variable and its value.
# --hints--
You should delete the `count = 0` line.
```js
({ test: () => assert.isFalse( /count\s*=\s*0/.test(code)) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(lowercase, r'[a-z]'),
(uppercase, r'[A-Z]'),
(special_chars, fr'[{symbols}]')
]
--fcc-editable-region--
# Check constraints
count = 0
if all(
constraint <= len(re.findall(pattern, password))
for constraint, pattern in constraints
):
--fcc-editable-region--
break
return password
# new_password = generate_password(8)
# print(new_password)
```
@@ -0,0 +1,77 @@
---
id: 657dadf4d8b93c1704f3a57c
title: Step 66
challengeType: 20
dashedName: step-66
---
# --description--
It works, but there are still a couple of things you can improve. First of all, calling a function with 5 arguments can create confusion on which value will be assigned to which parameter.
You can call a function using keyword arguments, that is writing the parameter name explicitly followed by the assignment operator and the value. For example:
```py
def add(x, y):
return x + y
spam(x=1, y=7) # 8
```
Modify your function call to use keyword arguments.
# --hints--
Your `new_password` variable should have the value of `generate_password(length=8, nums=1, special_chars=1, uppercase=1, lowercase=1)`.
```js
({ test: () => assert.match(code, /^new_password\s*=\s*generate_password\s*\(\s*length\s*=\s*8\s*,\s*nums\s*=\s*1\s*,\s*special_chars\s*=\s*1\s*,\s*uppercase\s*=\s*1\s*,\s*lowercase\s*=\s*1\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length, nums, special_chars, uppercase, lowercase):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(special_chars, fr'[{symbols}]'),
(uppercase, r'[A-Z]'),
(lowercase, r'[a-z]')
]
# Check constraints
if all(
constraint <= len(re.findall(pattern, password))
for constraint, pattern in constraints
):
break
return password
--fcc-editable-region--
new_password = generate_password(8, 1, 1, 1, 1)
print(new_password)
--fcc-editable-region--
```
@@ -0,0 +1,72 @@
---
id: 657db2114b4029241956f5d6
title: Step 68
challengeType: 20
dashedName: step-68
---
# --description--
Modify your function declaration to take default arguments. Use `16` for the `length` and `1` for the other constraints.
# --hints--
Your function should take default parameters.
```js
({ test: () => assert(__pyodide.runPython(`
import inspect
foo = __locals.get("generate_password")
sig = str(inspect.signature(foo))
sig == '(length=16, nums=1, special_chars=1, uppercase=1, lowercase=1)'
`))
})
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
--fcc-editable-region--
def generate_password(length, nums, special_chars, uppercase, lowercase):
--fcc-editable-region--
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(special_chars, fr'[{symbols}]'),
(uppercase, r'[A-Z]'),
(lowercase, r'[a-z]')
]
# Check constraints
if all(
constraint <= len(re.findall(pattern, password))
for constraint, pattern in constraints
):
break
return password
new_password = generate_password(nums=1, length=8, special_chars=1, uppercase=1, lowercase=1)
print(new_password)
```
@@ -0,0 +1,67 @@
---
id: 657db4cb77190e33a20e852a
title: Step 69
challengeType: 20
dashedName: step-69
---
# --description--
When you combine default arguments with keyword arguments, you are able to explicitly pass fewer arguments than those required by the function. The arguments that are not explicitly passed to the function call will take their default values.
Modify your `generate_password()` call to take only `length=8`.
# --hints--
You should pass only `length=8` to your `generate_password()` call.
```js
({ test: () => assert.match(code, /^new_password\s*=\s*generate_password\s*\(\s*length\s*=\s*8\s*\)/m) })
```
# --seed--
## --seed-contents--
```py
import re
import secrets
import string
def generate_password(length=16, nums=1, special_chars=1, uppercase=1, lowercase=1):
# Define the possible characters for the password
letters = string.ascii_letters
digits = string.digits
symbols = string.punctuation
# Combine all characters
all_characters = letters + digits + symbols
while True:
password = ''
# Generate password
for _ in range(length):
password += secrets.choice(all_characters)
constraints = [
(nums, r'\d'),
(special_chars, fr'[{symbols}]'),
(uppercase, r'[A-Z]'),
(lowercase, r'[a-z]')
]
# Check constraints
if all(
constraint <= len(re.findall(pattern, password))
for constraint, pattern in constraints
):
break
return password
--fcc-editable-region--
new_password = generate_password(nums=1, length=8, special_chars=1, uppercase=1, lowercase=1)
print(new_password)
--fcc-editable-region--
```