or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

browser.mdforms.mdindex.mdnavigation.mdutilities.md

utilities.mddocs/

0

# Utilities and Error Handling

1

2

Exception classes and utility functions for error handling and form analysis. These components provide robust error handling capabilities and helper functions for advanced form processing.

3

4

## Capabilities

5

6

### Exception Classes

7

8

Base exception classes for MechanicalSoup error handling.

9

10

```python { .api }

11

class LinkNotFoundError(Exception):

12

"""

13

Base exception for when MechanicalSoup fails to find elements.

14

15

Raised in scenarios such as:

16

- Link not found during navigation

17

- Form field not found

18

- 404 errors when raise_on_404=True in Browser

19

- Form or element selection failures

20

"""

21

22

class InvalidFormMethod(LinkNotFoundError):

23

"""

24

Exception raised when form method used on wrong element type.

25

26

Inherits from LinkNotFoundError and is typically raised when:

27

- Attempting form operations on non-form elements

28

- Invalid field type operations

29

- Form method validation failures

30

"""

31

```

32

33

**Usage Example:**

34

35

```python

36

import mechanicalsoup

37

38

browser = mechanicalsoup.StatefulBrowser(raise_on_404=True)

39

40

try:

41

# This will raise LinkNotFoundError if page not found

42

browser.open("https://httpbin.org/status/404")

43

except mechanicalsoup.LinkNotFoundError as e:

44

print(f"Page not found: {e}")

45

46

try:

47

# This will raise LinkNotFoundError if form not found

48

browser.select_form("nonexistent-form")

49

except mechanicalsoup.LinkNotFoundError as e:

50

print(f"Form selection failed: {e}")

51

52

try:

53

# This might raise InvalidFormMethod for invalid operations

54

browser.select_form()

55

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

56

except mechanicalsoup.InvalidFormMethod as e:

57

print(f"Invalid form operation: {e}")

58

```

59

60

### Form Analysis Utilities

61

62

Utility functions for analyzing form elements and upload capabilities.

63

64

```python { .api }

65

def is_multipart_file_upload(form, tag):

66

"""

67

Check if form element is a multipart file upload.

68

69

Analyzes form encoding and input types to determine if the form

70

supports file uploads through multipart/form-data encoding.

71

72

Parameters:

73

- form: BeautifulSoup form element to check

74

- tag: BeautifulSoup input tag to analyze

75

76

Returns:

77

bool: True if element supports multipart file upload, False otherwise

78

"""

79

```

80

81

**Usage Example:**

82

83

```python

84

import mechanicalsoup

85

from mechanicalsoup import is_multipart_file_upload

86

87

browser = mechanicalsoup.StatefulBrowser()

88

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

89

90

form_element = browser.page.find("form")

91

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

92

93

for input_tag in file_inputs:

94

if is_multipart_file_upload(form_element, input_tag):

95

print(f"File upload field found: {input_tag.get('name')}")

96

else:

97

print(f"Regular input field: {input_tag.get('name')}")

98

```

99

100

## Error Handling Patterns

101

102

### Graceful Navigation Error Handling

103

104

Handle navigation and page loading errors gracefully.

105

106

```python

107

import mechanicalsoup

108

109

def safe_navigate(browser, url, max_retries=3):

110

"""

111

Safely navigate to URL with retry logic and error handling.

112

"""

113

for attempt in range(max_retries):

114

try:

115

response = browser.open(url)

116

if response.status_code == 200:

117

return response

118

else:

119

print(f"HTTP {response.status_code} on attempt {attempt + 1}")

120

except mechanicalsoup.LinkNotFoundError as e:

121

print(f"Navigation failed on attempt {attempt + 1}: {e}")

122

if attempt == max_retries - 1:

123

raise

124

except Exception as e:

125

print(f"Unexpected error on attempt {attempt + 1}: {e}")

126

if attempt == max_retries - 1:

127

raise

128

129

return None

130

131

# Usage

132

browser = mechanicalsoup.StatefulBrowser(raise_on_404=True)

133

try:

134

response = safe_navigate(browser, "https://httpbin.org/status/500")

135

if response:

136

print("Navigation successful")

137

except mechanicalsoup.LinkNotFoundError:

138

print("All navigation attempts failed")

139

```

140

141

### Form Operation Error Handling

142

143

Handle form selection and field setting errors.

144

145

```python

146

import mechanicalsoup

147

148

def safe_form_fill(browser, form_data, form_selector="form"):

149

"""

150

Safely fill form with comprehensive error handling.

151

"""

152

try:

153

# Select form with error handling

154

browser.select_form(form_selector)

155

if not browser.form:

156

raise mechanicalsoup.LinkNotFoundError(f"No form found with selector: {form_selector}")

157

158

# Fill form fields with individual error handling

159

for field_name, field_value in form_data.items():

160

try:

161

browser[field_name] = field_value

162

print(f"Set {field_name} = {field_value}")

163

except mechanicalsoup.InvalidFormMethod as e:

164

print(f"Failed to set field {field_name}: {e}")

165

# Try force setting as fallback

166

try:

167

browser.form.set(field_name, field_value, force=True)

168

print(f"Force set {field_name} = {field_value}")

169

except Exception as force_error:

170

print(f"Force setting also failed for {field_name}: {force_error}")

171

172

return True

173

174

except mechanicalsoup.LinkNotFoundError as e:

175

print(f"Form selection failed: {e}")

176

return False

177

except Exception as e:

178

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

179

return False

180

181

# Usage

182

browser = mechanicalsoup.StatefulBrowser()

183

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

184

185

form_data = {

186

"custname": "Test User",

187

"custtel": "555-0000",

188

"nonexistent_field": "test_value"

189

}

190

191

if safe_form_fill(browser, form_data):

192

try:

193

response = browser.submit_selected()

194

print("Form submitted successfully")

195

except Exception as e:

196

print(f"Form submission failed: {e}")

197

```

198

199

### Link Following Error Handling

200

201

Handle link discovery and following with fallback strategies.

202

203

```python

204

import mechanicalsoup

205

import re

206

207

def follow_link_safe(browser, search_criteria, fallback_url=None):

208

"""

209

Safely follow links with multiple search strategies and fallbacks.

210

"""

211

try:

212

# Try to find link by various criteria

213

link = None

214

215

if isinstance(search_criteria, str):

216

# Search by text

217

link = browser.find_link(link_text=search_criteria)

218

elif isinstance(search_criteria, dict):

219

# Search by multiple criteria

220

link = browser.find_link(**search_criteria)

221

222

if link:

223

browser.follow_link(link)

224

print(f"Successfully followed link to: {browser.url}")

225

return True

226

else:

227

print("Link not found with given criteria")

228

229

# Fallback to direct URL if provided

230

if fallback_url:

231

print(f"Using fallback URL: {fallback_url}")

232

browser.open(fallback_url)

233

return True

234

235

except mechanicalsoup.LinkNotFoundError as e:

236

print(f"Link following failed: {e}")

237

if fallback_url:

238

print(f"Using fallback URL: {fallback_url}")

239

browser.open(fallback_url)

240

return True

241

except Exception as e:

242

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

243

244

return False

245

246

# Usage

247

browser = mechanicalsoup.StatefulBrowser()

248

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

249

250

# Try multiple search strategies

251

search_strategies = [

252

"JSON", # Search by text

253

{"url_regex": re.compile(r"/json")}, # Search by URL pattern

254

{"href": "/json"} # Search by exact href

255

]

256

257

for strategy in search_strategies:

258

if follow_link_safe(browser, strategy, fallback_url="https://httpbin.org/json"):

259

break

260

```

261

262

### File Upload Handling

263

264

Handle file upload forms with proper multipart detection.

265

266

```python

267

import mechanicalsoup

268

from mechanicalsoup import is_multipart_file_upload

269

import os

270

271

def handle_file_upload_form(browser, file_field_name, file_path, form_data=None):

272

"""

273

Handle file upload forms with proper validation and error handling.

274

"""

275

try:

276

# Ensure form is selected

277

if not browser.form:

278

browser.select_form()

279

280

form_element = browser.form.form

281

file_input = form_element.find("input", {"name": file_field_name, "type": "file"})

282

283

if not file_input:

284

raise mechanicalsoup.InvalidFormMethod(f"File input '{file_field_name}' not found")

285

286

# Verify multipart capability

287

if not is_multipart_file_upload(form_element, file_input):

288

print("Warning: Form may not support file uploads properly")

289

290

# Check file exists

291

if not os.path.exists(file_path):

292

raise FileNotFoundError(f"File not found: {file_path}")

293

294

# Fill other form fields if provided

295

if form_data:

296

for field_name, field_value in form_data.items():

297

browser[field_name] = field_value

298

299

# Handle file upload (typically done through requests)

300

with open(file_path, 'rb') as file:

301

files = {file_field_name: file}

302

response = browser.session.post(

303

browser.absolute_url(form_element.get('action', '')),

304

files=files,

305

data={input.get('name'): input.get('value', '')

306

for input in form_element.find_all('input')

307

if input.get('type') != 'file'}

308

)

309

310

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

311

return response

312

313

except mechanicalsoup.InvalidFormMethod as e:

314

print(f"Invalid form method for file upload: {e}")

315

except FileNotFoundError as e:

316

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

317

except Exception as e:

318

print(f"File upload error: {e}")

319

320

return None

321

322

# Usage example (conceptual - would need actual file upload form)

323

browser = mechanicalsoup.StatefulBrowser()

324

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

325

#

326

# response = handle_file_upload_form(

327

# browser,

328

# file_field_name="document",

329

# file_path="/path/to/document.pdf",

330

# form_data={"description": "Important document"}

331

# )

332

```

333

334

## Exception Hierarchy

335

336

```python

337

Exception

338

└── LinkNotFoundError

339

└── InvalidFormMethod

340

```

341

342

All MechanicalSoup-specific exceptions inherit from `LinkNotFoundError`, making it easy to catch all library-related errors:

343

344

```python

345

import mechanicalsoup

346

347

browser = mechanicalsoup.StatefulBrowser(raise_on_404=True)

348

349

try:

350

browser.open("https://httpbin.org/status/404")

351

browser.select_form("nonexistent")

352

browser["field"] = "value"

353

browser.submit_selected()

354

except mechanicalsoup.LinkNotFoundError as e:

355

# Catches both LinkNotFoundError and InvalidFormMethod

356

print(f"MechanicalSoup operation failed: {e}")

357

except Exception as e:

358

# Catches other unexpected errors

359

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

360

```

361

362

## Version Information

363

364

Access the MechanicalSoup package version.

365

366

```python { .api }

367

__version__: str

368

"""The version string of the installed MechanicalSoup package."""

369

```

370

371

**Usage Example:**

372

373

```python

374

import mechanicalsoup

375

376

# Check the installed version

377

print(f"MechanicalSoup version: {mechanicalsoup.__version__}")

378

379

# Use version for compatibility checks

380

from packaging import version

381

if version.parse(mechanicalsoup.__version__) >= version.parse("1.4.0"):

382

print("Using modern MechanicalSoup with latest features")

383

else:

384

print("Consider upgrading MechanicalSoup")

385

```