or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

browser.mdforms.mdindex.mdnavigation.mdutilities.md

forms.mddocs/

0

# Form Handling

1

2

HTML form manipulation and field setting capabilities. The Form class provides comprehensive support for all standard form elements including inputs, checkboxes, radio buttons, selects, and textareas with automatic type detection and validation.

3

4

## Capabilities

5

6

### Form Creation

7

8

Create Form instances from BeautifulSoup form elements.

9

10

```python { .api }

11

class Form:

12

def __init__(self, form):

13

"""

14

Create a Form instance from a BeautifulSoup form element.

15

16

Parameters:

17

- form: bs4.element.Tag representing a form element

18

"""

19

```

20

21

**Usage Example:**

22

23

```python

24

import mechanicalsoup

25

from mechanicalsoup import Form

26

27

browser = mechanicalsoup.StatefulBrowser()

28

response = browser.open("https://httpbin.org/forms/post")

29

30

# Create form from BeautifulSoup element

31

form_element = response.soup.find("form")

32

form = Form(form_element)

33

34

# Or use StatefulBrowser to select and get form automatically

35

browser.select_form()

36

form = browser.form

37

```

38

39

### Universal Field Setting

40

41

Auto-detect field types and set values appropriately.

42

43

```python { .api }

44

def set(self, name, value, force=False):

45

"""

46

Auto-detect field type and set value.

47

48

Parameters:

49

- name: Field name attribute

50

- value: Value to set

51

- force: If True, force setting even if field not found

52

"""

53

54

def __setitem__(self, name, value):

55

"""Set form field value using bracket notation (calls set())"""

56

```

57

58

**Usage Example:**

59

60

```python

61

form = Form(form_element)

62

63

# Auto-detect and set any field type

64

form.set("username", "john_doe")

65

form.set("age", 25)

66

form.set("newsletter", True)

67

68

# Using bracket notation (recommended)

69

form["username"] = "john_doe"

70

form["age"] = 25

71

form["newsletter"] = True

72

```

73

74

### Input Field Handling

75

76

Handle text inputs, hidden fields, and other input elements.

77

78

```python { .api }

79

def set_input(self, data):

80

"""

81

Set input field values from dictionary.

82

83

Parameters:

84

- data: Dict mapping field names to values

85

"""

86

```

87

88

**Usage Example:**

89

90

```python

91

form = Form(form_element)

92

93

# Set multiple input fields at once

94

form.set_input({

95

"first_name": "John",

96

"last_name": "Doe",

97

"email": "john@example.com",

98

"phone": "555-1234"

99

})

100

101

# Individual field setting

102

form["password"] = "secret123"

103

form["confirm_password"] = "secret123"

104

```

105

106

### Checkbox Handling

107

108

Manage checkbox selections with flexible options.

109

110

```python { .api }

111

def set_checkbox(self, data, uncheck_other_boxes=True):

112

"""

113

Check/uncheck checkboxes.

114

115

Parameters:

116

- data: Dict mapping checkbox names to boolean values,

117

or dict mapping names to lists of values for multi-value checkboxes

118

- uncheck_other_boxes: Whether to uncheck other checkboxes with same name

119

"""

120

121

def check(self, data):

122

"""Backward-compatible checkbox/radio setting method"""

123

124

def uncheck_all(self, name):

125

"""

126

Uncheck all checkboxes with given name.

127

128

Parameters:

129

- name: Checkbox name attribute

130

"""

131

```

132

133

**Usage Example:**

134

135

```python

136

form = Form(form_element)

137

138

# Check/uncheck single checkboxes

139

form.set_checkbox({

140

"subscribe_newsletter": True,

141

"accept_terms": True,

142

"marketing_emails": False

143

})

144

145

# Handle multi-value checkboxes (multiple checkboxes with same name)

146

form.set_checkbox({

147

"interests": ["sports", "music", "travel"]

148

})

149

150

# Uncheck all checkboxes with specific name

151

form.uncheck_all("previous_selections")

152

153

# Legacy method

154

form.check({"newsletter": True})

155

```

156

157

### Radio Button Handling

158

159

Select radio button options.

160

161

```python { .api }

162

def set_radio(self, data):

163

"""

164

Select radio button options.

165

166

Parameters:

167

- data: Dict mapping radio group names to selected values

168

"""

169

```

170

171

**Usage Example:**

172

173

```python

174

form = Form(form_element)

175

176

# Select radio button options

177

form.set_radio({

178

"gender": "female",

179

"age_group": "25-34",

180

"preferred_contact": "email"

181

})

182

183

# Can also use universal set method

184

form["size"] = "large" # Works for radio buttons too

185

```

186

187

### Textarea Handling

188

189

Set content for textarea elements.

190

191

```python { .api }

192

def set_textarea(self, data):

193

"""

194

Set textarea content.

195

196

Parameters:

197

- data: Dict mapping textarea names to content strings

198

"""

199

```

200

201

**Usage Example:**

202

203

```python

204

form = Form(form_element)

205

206

# Set textarea content

207

form.set_textarea({

208

"comments": "This is a multi-line comment\nwith line breaks.",

209

"bio": "Software developer with 5 years experience..."

210

})

211

212

# Using universal set method

213

form["message"] = "Hello, this works for textareas too!"

214

```

215

216

### Select/Dropdown Handling

217

218

Handle select elements and option selection.

219

220

```python { .api }

221

def set_select(self, data):

222

"""

223

Select dropdown options.

224

225

Parameters:

226

- data: Dict mapping select names to selected values,

227

or lists of values for multi-select elements

228

"""

229

```

230

231

**Usage Example:**

232

233

```python

234

form = Form(form_element)

235

236

# Single select dropdown

237

form.set_select({

238

"country": "United States",

239

"state": "California",

240

"priority": "high"

241

})

242

243

# Multi-select elements

244

form.set_select({

245

"languages": ["Python", "JavaScript", "Go"],

246

"skills": ["web_dev", "databases", "apis"]

247

})

248

249

# Using universal method

250

form["department"] = "Engineering"

251

```

252

253

### Form Control Management

254

255

Add new form controls and manage submit buttons.

256

257

```python { .api }

258

def new_control(self, type, name, value, **kwargs):

259

"""

260

Add new input element to form.

261

262

Parameters:

263

- type: Input type (text, hidden, submit, etc.)

264

- name: Control name attribute

265

- value: Control value attribute

266

- **kwargs: Additional HTML attributes

267

"""

268

269

def choose_submit(self, submit):

270

"""

271

Select which submit button to use for form submission.

272

273

Parameters:

274

- submit: Submit button element or name/value criteria

275

"""

276

```

277

278

**Usage Example:**

279

280

```python

281

form = Form(form_element)

282

283

# Add hidden fields

284

form.new_control("hidden", "csrf_token", "abc123")

285

form.new_control("hidden", "session_id", "xyz789")

286

287

# Add custom input with attributes

288

form.new_control("text", "dynamic_field", "",

289

placeholder="Enter value",

290

class_="custom-input")

291

292

# Choose specific submit button

293

form.choose_submit("save_draft") # Use button with name="save_draft"

294

```

295

296

### Form Debugging

297

298

Debug and inspect form structure.

299

300

```python { .api }

301

def print_summary(self):

302

"""Print form structure and field information for debugging"""

303

```

304

305

**Usage Example:**

306

307

```python

308

form = Form(form_element)

309

310

# Print complete form structure

311

form.print_summary()

312

313

# Example output:

314

# Form summary:

315

# action: /submit

316

# method: POST

317

# Fields:

318

# - username (text): ""

319

# - password (password): ""

320

# - remember_me (checkbox): unchecked

321

# - submit (submit): "Login"

322

```

323

324

### Legacy Compatibility Methods

325

326

Deprecated methods maintained for backward compatibility.

327

328

```python { .api }

329

def attach(self):

330

"""Deprecated: Use set_input() instead"""

331

332

def input(self):

333

"""Deprecated: Use set_input() instead"""

334

335

def textarea(self):

336

"""Deprecated: Use set_textarea() instead"""

337

```

338

339

## Public Attributes

340

341

```python { .api }

342

# Form instance attributes

343

form: bs4.element.Tag # The underlying BeautifulSoup form element

344

```

345

346

## Complete Form Handling Example

347

348

```python

349

import mechanicalsoup

350

351

# Navigate to form page

352

browser = mechanicalsoup.StatefulBrowser()

353

browser.open("https://httpbin.org/forms/post")

354

355

# Select and examine form

356

browser.select_form('form[action="/post"]')

357

form = browser.form

358

359

# Print form structure for debugging

360

form.print_summary()

361

362

# Fill various field types

363

form["custname"] = "Alice Johnson"

364

form["custtel"] = "555-0123"

365

366

# Handle radio buttons and checkboxes

367

form.set_radio({"size": "medium"})

368

form.set_checkbox({"newsletter": True})

369

370

# Add dynamic fields

371

form.new_control("hidden", "source", "web_form")

372

form.new_control("hidden", "timestamp", "2024-01-15T10:30:00Z")

373

374

# Set textarea content

375

form.set_textarea({"comments": "Great service, very satisfied!"})

376

377

# Choose specific submit button if multiple exist

378

form.choose_submit("submit_order")

379

380

# Submit form

381

response = browser.submit_selected()

382

print("Form submitted:", response.json())

383

```

384

385

## Form Validation and Error Handling

386

387

```python

388

import mechanicalsoup

389

390

try:

391

browser = mechanicalsoup.StatefulBrowser()

392

browser.open("https://httpbin.org/forms/post")

393

394

# Select form

395

browser.select_form()

396

397

# Attempt to set non-existent field

398

try:

399

browser.form.set("nonexistent_field", "value", force=False)

400

except mechanicalsoup.InvalidFormMethod:

401

print("Field not found in form")

402

403

# Force setting (creates new field)

404

browser.form.set("nonexistent_field", "value", force=True)

405

406

# Submit and handle errors

407

response = browser.submit_selected()

408

409

except mechanicalsoup.LinkNotFoundError as e:

410

print(f"Navigation error: {e}")

411

except Exception as e:

412

print(f"Unexpected error: {e}")

413

```

414

415

## Secure File Upload Handling

416

417

File uploads in forms require special security considerations. Since MechanicalSoup v1.3.0, file inputs must be open file objects (IOBase) rather than file paths to prevent security vulnerabilities.

418

419

```python { .api }

420

def _assert_valid_file_upload(self, tag, value):

421

"""

422

Internal validation for secure file uploads.

423

424

Raises ValueError if attempting to upload non-IOBase objects to

425

multipart file inputs (security mitigation for CVE-2023-34457).

426

"""

427

```

428

429

**Secure File Upload Example:**

430

431

```python

432

import mechanicalsoup

433

import io

434

from mechanicalsoup import is_multipart_file_upload

435

436

def secure_file_upload_example():

437

"""

438

Demonstrate secure file upload handling.

439

"""

440

browser = mechanicalsoup.StatefulBrowser()

441

442

# Navigate to a form with file upload

443

# browser.open("https://example.com/upload-form")

444

# browser.select_form()

445

446

# SECURE: Use open file objects directly

447

with open("/path/to/document.pdf", "rb") as file_obj:

448

# This is the secure way (v1.3.0+)

449

browser["document"] = file_obj

450

451

# Fill other form fields

452

browser["description"] = "Important document"

453

browser["category"] = "reports"

454

455

# Submit while file is still open

456

response = browser.submit_selected()

457

print(f"Upload successful: {response.status_code}")

458

459

# Alternative: Create in-memory file

460

file_content = b"Hello, this is file content"

461

file_obj = io.BytesIO(file_content)

462

file_obj.name = "test.txt" # Optional: set filename

463

464

browser["text_file"] = file_obj

465

response = browser.submit_selected()

466

467

# INSECURE: These patterns will raise ValueError in v1.3.0+

468

def insecure_patterns_to_avoid():

469

"""

470

Examples of patterns that will raise security exceptions.

471

"""

472

browser = mechanicalsoup.StatefulBrowser()

473

# browser.open("https://example.com/upload-form")

474

# browser.select_form()

475

476

try:

477

# This will raise ValueError (CVE-2023-34457 mitigation)

478

browser["document"] = "/path/to/file.pdf" # String path - INSECURE

479

except ValueError as e:

480

print(f"Security error: {e}")

481

482

try:

483

# This will also raise ValueError

484

browser["document"] = b"raw bytes" # Bytes - INSECURE

485

except ValueError as e:

486

print(f"Security error: {e}")

487

488

# Check if form supports file uploads

489

def check_file_upload_support():

490

"""

491

Verify form supports secure file uploads.

492

"""

493

browser = mechanicalsoup.StatefulBrowser()

494

# browser.open("https://example.com/form")

495

# browser.select_form()

496

497

form_element = browser.form.form

498

file_inputs = form_element.find_all("input", {"type": "file"})

499

500

for file_input in file_inputs:

501

field_name = file_input.get("name")

502

if is_multipart_file_upload(form_element, file_input):

503

print(f"Field '{field_name}' supports secure file upload")

504

else:

505

print(f"Warning: Field '{field_name}' may not support file upload properly")

506

507

# Usage examples

508

if __name__ == "__main__":

509

secure_file_upload_example()

510

check_file_upload_support()

511

```

512

513

**Key Security Notes:**

514

515

- **Always use open file objects** (`IOBase` instances) for file uploads

516

- **Never pass file paths as strings** - this creates security vulnerabilities

517

- **Use context managers** (`with open(...)`) to ensure files are properly closed

518

- **Test multipart support** with `is_multipart_file_upload()` before uploading

519

- **Handle `ValueError` exceptions** for invalid file upload attempts