Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

383

384

385

386

387

388

389

390

391

392

393

394

395

396

397

398

399

400

401

402

403

404

405

406

407

408

409

410

411

412

413

414

415

416

417

418

419

420

421

422

423

424

425

426

427

428

429

430

431

432

433

434

435

436

437

438

439

440

441

442

443

444

445

446

447

448

449

450

451

452

453

454

455

456

457

458

459

460

461

462

463

464

465

466

467

468

469

470

471

472

473

474

475

476

477

478

479

480

481

482

483

484

485

486

487

488

489

490

491

492

493

494

495

496

497

498

499

500

501

502

503

504

505

506

507

508

509

510

511

512

513

514

515

516

517

518

519

520

521

522

523

524

525

526

527

528

529

530

531

532

533

534

535

536

537

538

539

540

541

542

543

544

545

546

547

548

549

550

551

552

553

554

555

556

557

558

559

560

561

562

563

564

565

566

567

568

569

570

571

572

573

574

575

576

577

578

579

580

581

582

583

584

585

586

587

588

589

590

591

592

593

594

595

596

597

598

599

600

601

602

603

604

605

606

607

608

609

610

611

612

613

614

615

616

617

618

619

620

621

622

623

624

625

626

627

628

629

630

631

632

633

634

635

636

637

638

639

640

641

642

643

644

645

646

647

648

649

650

651

652

653

654

655

656

657

658

659

660

661

662

663

664

665

666

667

668

669

670

671

672

673

674

675

676

677

678

679

680

681

682

683

684

685

686

687

688

689

690

691

692

693

694

695

696

697

698

699

700

701

702

703

704

705

706

707

708

709

710

711

712

713

714

715

716

717

718

719

720

721

722

723

724

725

726

727

728

729

730

731

732

733

734

735

736

737

738

739

740

741

742

743

744

745

746

747

748

749

750

751

752

753

754

755

756

757

758

759

760

761

762

763

764

765

766

767

768

769

770

771

772

773

774

775

776

777

778

779

780

781

782

783

784

785

786

787

788

789

790

791

792

793

794

795

796

797

798

799

800

801

802

803

804

805

806

807

808

809

810

811

812

813

814

815

816

817

818

819

820

821

822

823

824

825

826

827

828

829

830

831

832

833

834

835

836

837

838

839

840

841

842

843

844

845

846

847

848

849

850

851

852

853

854

855

856

857

858

859

860

861

862

863

864

865

866

867

868

869

870

871

872

873

874

875

876

877

878

879

880

881

882

883

884

885

886

887

888

889

890

891

892

893

894

895

896

897

898

899

900

901

902

903

904

905

906

907

908

909

910

911

912

913

914

915

916

917

918

919

920

921

922

923

924

925

926

927

928

929

930

931

932

933

934

935

936

937

938

939

940

941

942

943

944

945

946

947

948

949

950

951

952

953

954

955

956

957

958

959

960

961

962

963

964

965

966

967

968

969

970

971

972

973

974

975

976

977

978

979

980

981

982

983

984

985

986

987

988

989

990

991

992

993

994

995

996

997

998

999

1000

1001

1002

1003

1004

1005

1006

1007

1008

1009

1010

1011

1012

1013

1014

1015

1016

1017

1018

1019

1020

1021

1022

1023

1024

1025

1026

1027

1028

1029

1030

1031

1032

1033

1034

1035

1036

1037

1038

1039

1040

1041

1042

1043

1044

1045

1046

1047

1048

1049

1050

1051

1052

1053

1054

1055

1056

1057

1058

1059

1060

1061

1062

1063

1064

1065

1066

1067

1068

1069

1070

1071

1072

1073

1074

1075

1076

1077

1078

1079

1080

1081

1082

1083

1084

1085

1086

1087

1088

1089

1090

1091

1092

1093

1094

1095

1096

1097

1098

1099

1100

1101

1102

1103

1104

1105

1106

1107

1108

1109

1110

1111

1112

1113

1114

1115

1116

1117

1118

1119

1120

1121

1122

1123

1124

1125

1126

1127

1128

1129

1130

1131

1132

1133

1134

1135

1136

1137

1138

1139

1140

1141

1142

1143

1144

1145

1146

1147

1148

1149

1150

1151

1152

1153

1154

1155

1156

1157

1158

1159

1160

1161

1162

1163

1164

1165

1166

1167

1168

1169

1170

1171

1172

1173

1174

1175

1176

1177

1178

1179

1180

1181

1182

1183

1184

1185

1186

1187

1188

1189

1190

1191

1192

1193

1194

1195

1196

1197

1198

1199

1200

1201

1202

1203

1204

1205

1206

1207

1208

1209

1210

1211

1212

1213

1214

1215

1216

1217

1218

1219

1220

1221

1222

1223

1224

1225

1226

1227

1228

1229

1230

1231

1232

1233

1234

1235

1236

1237

1238

1239

1240

1241

1242

1243

1244

1245

1246

1247

1248

1249

1250

1251

1252

1253

1254

1255

1256

1257

1258

1259

1260

1261

1262

1263

1264

1265

1266

1267

1268

1269

1270

1271

1272

1273

1274

1275

1276

1277

1278

1279

1280

1281

1282

1283

1284

1285

1286

1287

1288

1289

1290

1291

1292

1293

1294

1295

1296

1297

1298

1299

1300

1301

1302

1303

1304

1305

1306

1307

1308

1309

1310

1311

1312

1313

1314

1315

1316

1317

1318

1319

1320

1321

1322

1323

1324

1325

1326

1327

1328

1329

1330

1331

1332

1333

1334

1335

1336

1337

1338

1339

1340

1341

1342

1343

1344

1345

1346

1347

1348

1349

1350

1351

1352

1353

1354

1355

1356

1357

1358

1359

1360

1361

1362

1363

1364

1365

1366

1367

1368

1369

1370

1371

1372

1373

1374

1375

1376

1377

1378

1379

1380

1381

1382

1383

1384

1385

1386

1387

1388

1389

1390

1391

1392

1393

1394

1395

1396

1397

1398

1399

1400

1401

1402

1403

1404

1405

1406

1407

1408

1409

1410

1411

1412

1413

1414

1415

1416

1417

1418

1419

1420

1421

1422

1423

1424

1425

1426

1427

1428

1429

1430

1431

1432

1433

1434

1435

1436

1437

1438

1439

1440

1441

1442

1443

1444

1445

1446

1447

1448

1449

1450

1451

1452

1453

1454

1455

1456

1457

1458

1459

1460

1461

1462

1463

1464

1465

1466

1467

1468

1469

1470

1471

1472

1473

1474

1475

1476

1477

1478

1479

1480

1481

1482

1483

1484

1485

1486

1487

1488

1489

1490

1491

1492

1493

1494

1495

1496

1497

1498

1499

1500

1501

1502

1503

1504

1505

1506

1507

1508

1509

1510

1511

1512

1513

1514

1515

1516

1517

1518

1519

1520

1521

1522

1523

1524

1525

1526

1527

1528

1529

1530

1531

1532

1533

1534

1535

1536

1537

1538

1539

1540

1541

1542

1543

1544

1545

1546

1547

1548

1549

1550

1551

1552

1553

1554

1555

1556

1557

1558

1559

1560

1561

1562

1563

1564

1565

1566

1567

1568

1569

1570

1571

1572

1573

1574

1575

1576

1577

1578

1579

1580

1581

1582

1583

1584

1585

1586

1587

1588

1589

1590

1591

1592

1593

1594

1595

1596

1597

1598

1599

1600

1601

1602

1603

1604

1605

1606

1607

1608

1609

1610

""" 

Classes for sources of doctests 

 

This module defines various classes for sources from which doctests 

originate, such as files, functions or database entries. 

 

AUTHORS: 

 

- David Roe (2012-03-27) -- initial version, based on Robert Bradshaw's code. 

""" 

 

#***************************************************************************** 

# Copyright (C) 2012 David Roe <roed.math@gmail.com> 

# Robert Bradshaw <robertwb@gmail.com> 

# William Stein <wstein@gmail.com> 

# 

# Distributed under the terms of the GNU General Public License (GPL) 

# 

# http://www.gnu.org/licenses/ 

#***************************************************************************** 

from __future__ import print_function, absolute_import 

 

import os 

import sys 

import re 

import random 

import doctest 

from Cython.Utils import is_package_dir 

from sage.cpython.string import bytes_to_str 

from sage.repl.preparse import preparse 

from sage.repl.load import load 

from sage.misc.lazy_attribute import lazy_attribute 

from .parsing import SageDocTestParser 

from .util import NestedName 

from sage.structure.dynamic_class import dynamic_class 

from sage.env import SAGE_SRC, SAGE_LOCAL 

 

# Python file parsing 

triple_quotes = re.compile("\s*[rRuU]*((''')|(\"\"\"))") 

name_regex = re.compile(r".*\s(\w+)([(].*)?:") 

 

# LaTeX file parsing 

begin_verb = re.compile(r"\s*\\begin{verbatim}") 

end_verb = re.compile(r"\s*\\end{verbatim}\s*(%link)?") 

begin_lstli = re.compile(r"\s*\\begin{lstlisting}") 

end_lstli = re.compile(r"\s*\\end{lstlisting}\s*(%link)?") 

skip = re.compile(r".*%skip.*") 

 

# ReST file parsing 

link_all = re.compile(r"^\s*\.\.\s+linkall\s*$") 

double_colon = re.compile(r"^(\s*).*::\s*$") 

 

whitespace = re.compile("\s*") 

bitness_marker = re.compile('#.*(32|64)-bit') 

bitness_value = '64' if sys.maxsize > (1 << 32) else '32' 

 

# For neutralizing doctests 

find_prompt = re.compile(r"^(\s*)(>>>|sage:)(.*)") 

 

# For testing that enough doctests are created 

sagestart = re.compile(r"^\s*(>>> |sage: )\s*[^#\s]") 

untested = re.compile("(not implemented|not tested)") 

 

# For parsing a PEP 0263 encoding declaration 

pep_0263 = re.compile(br'^[ \t\v]*#.*?coding[:=]\s*([-\w.]+)') 

 

# Source line number in warning output 

doctest_line_number = re.compile(r"^\s*doctest:[0-9]") 

 

 

def get_basename(path): 

""" 

This function returns the basename of the given path, e.g. sage.doctest.sources or doc.ru.tutorial.tour_advanced 

 

EXAMPLES:: 

 

sage: from sage.doctest.sources import get_basename 

sage: from sage.env import SAGE_SRC 

sage: import os 

sage: get_basename(os.path.join(SAGE_SRC,'sage','doctest','sources.py')) 

'sage.doctest.sources' 

""" 

if path is None: 

return None 

if not os.path.exists(path): 

return path 

path = os.path.abspath(path) 

root = os.path.dirname(path) 

# If the file is in the sage library, we can use our knowledge of 

# the directory structure 

dev = SAGE_SRC 

sp = os.path.join(SAGE_LOCAL, 'lib', 'python', 'site-packages') 

if path.startswith(dev): 

# there will be a branch name 

i = path.find(os.path.sep, len(dev)) 

if i == -1: 

# this source is the whole library.... 

return path 

root = path[:i] 

elif path.startswith(sp): 

root = path[:len(sp)] 

else: 

# If this file is in some python package we can see how deep 

# it goes by the presence of __init__.py files. 

while os.path.exists(os.path.join(root, '__init__.py')): 

root = os.path.dirname(root) 

fully_qualified_path = os.path.splitext(path[len(root) + 1:])[0] 

if os.path.split(path)[1] == '__init__.py': 

fully_qualified_path = fully_qualified_path[:-9] 

return fully_qualified_path.replace(os.path.sep, '.') 

 

class DocTestSource(object): 

""" 

This class provides a common base class for different sources of doctests. 

 

INPUT: 

 

- ``options`` -- a :class:`sage.doctest.control.DocTestDefaults` 

instance or equivalent. 

""" 

def __init__(self, options): 

""" 

Initialization. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: from sage.env import SAGE_SRC 

sage: import os 

sage: filename = os.path.join(SAGE_SRC,'sage','doctest','sources.py') 

sage: FDS = FileDocTestSource(filename,DocTestDefaults()) 

sage: TestSuite(FDS).run() 

""" 

self.options = options 

 

def __eq__(self, other): 

""" 

Comparison is just by comparison of attributes. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: from sage.env import SAGE_SRC 

sage: import os 

sage: filename = os.path.join(SAGE_SRC,'sage','doctest','sources.py') 

sage: DD = DocTestDefaults() 

sage: FDS = FileDocTestSource(filename,DD) 

sage: FDS2 = FileDocTestSource(filename,DD) 

sage: FDS == FDS2 

True 

""" 

if type(self) != type(other): 

return False 

return self.__dict__ == other.__dict__ 

 

def __ne__(self, other): 

""" 

Test for unequality. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: from sage.env import SAGE_SRC 

sage: import os 

sage: filename = os.path.join(SAGE_SRC,'sage','doctest','sources.py') 

sage: DD = DocTestDefaults() 

sage: FDS = FileDocTestSource(filename,DD) 

sage: FDS2 = FileDocTestSource(filename,DD) 

sage: FDS != FDS2 

False 

""" 

return not (self == other) 

 

def _process_doc(self, doctests, doc, namespace, start): 

""" 

Appends doctests defined in ``doc`` to the list ``doctests``. 

 

This function is called when a docstring block is completed 

(either by ending a triple quoted string in a Python file, 

unindenting from a comment block in a ReST file, or ending a 

verbatim or lstlisting environment in a LaTeX file). 

 

INPUT: 

 

- ``doctests`` -- a running list of doctests to which the new 

test(s) will be appended. 

 

- ``doc`` -- a list of lines of a docstring, each including 

the trailing newline. 

 

- ``namespace`` -- a dictionary or 

:class:`sage.doctest.util.RecordingDict`, used in the 

creation of new :class:`doctest.DocTest`s. 

 

- ``start`` -- an integer, giving the line number of the start 

of this docstring in the larger file. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: from sage.doctest.parsing import SageDocTestParser 

sage: from sage.env import SAGE_SRC 

sage: import os 

sage: filename = os.path.join(SAGE_SRC,'sage','doctest','util.py') 

sage: FDS = FileDocTestSource(filename,DocTestDefaults()) 

sage: doctests, _ = FDS.create_doctests({}) 

sage: manual_doctests = [] 

sage: for dt in doctests: 

....: FDS.qualified_name = dt.name 

....: FDS._process_doc(manual_doctests, dt.docstring, {}, dt.lineno-1) 

sage: doctests == manual_doctests 

True 

""" 

docstring = "".join(doc) 

new_doctests = self.parse_docstring(docstring, namespace, start) 

sig_on_count_doc_doctest = "sig_on_count() # check sig_on/off pairings (virtual doctest)\n" 

for dt in new_doctests: 

if len(dt.examples) > 0 and not (hasattr(dt.examples[-1],'sage_source') 

and dt.examples[-1].sage_source == sig_on_count_doc_doctest): 

# Line number refers to the end of the docstring 

sigon = doctest.Example(sig_on_count_doc_doctest, "0\n", lineno=docstring.count("\n")) 

sigon.sage_source = sig_on_count_doc_doctest 

dt.examples.append(sigon) 

doctests.append(dt) 

 

def _create_doctests(self, namespace, tab_okay=None): 

""" 

Creates a list of doctests defined in this source. 

 

This function collects functionality common to file and string 

sources, and is called by 

:meth:`FileDocTestSource.create_doctests`. 

 

INPUT: 

 

- ``namespace`` -- a dictionary or 

:class:`sage.doctest.util.RecordingDict`, used in the 

creation of new :class:`doctest.DocTest`s. 

 

- ``tab_okay`` -- whether tabs are allowed in this source. 

 

OUTPUT: 

 

- ``doctests`` -- a list of doctests defined by this source 

 

- ``extras`` -- a dictionary with ``extras['tab']`` either 

False or a list of linenumbers on which tabs appear. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: from sage.doctest.util import NestedName 

sage: from sage.env import SAGE_SRC 

sage: import os 

sage: filename = os.path.join(SAGE_SRC,'sage','doctest','sources.py') 

sage: FDS = FileDocTestSource(filename,DocTestDefaults()) 

sage: FDS.qualified_name = NestedName('sage.doctest.sources') 

sage: doctests, extras = FDS._create_doctests({}) 

sage: len(doctests) 

41 

sage: extras['tab'] 

False 

sage: extras['line_number'] 

False 

""" 

if tab_okay is None: 

tab_okay = isinstance(self,TexSource) 

self._init() 

self.line_shift = 0 

self.parser = SageDocTestParser(self.options.long, self.options.optional) 

self.linking = False 

doctests = [] 

in_docstring = False 

tab_found = False 

unparsed_doc = False 

doc = [] 

start = None 

tab_locations = [] 

contains_line_number = False 

for lineno, line in self: 

if doctest_line_number.search(line) is not None: 

contains_line_number = True 

if "\t" in line: 

tab_locations.append(str(lineno+1)) 

if "SAGE_DOCTEST_ALLOW_TABS" in line: 

tab_okay = True 

just_finished = False 

if in_docstring: 

if self.ending_docstring(line): 

in_docstring = False 

just_finished = True 

self._process_doc(doctests, doc, namespace, start) 

unparsed_doc = False 

else: 

bitness = bitness_marker.search(line) 

if bitness: 

if bitness.groups()[0] != bitness_value: 

self.line_shift += 1 

continue 

else: 

line = line[:bitness.start()] + "\n" 

if self.line_shift and sagestart.match(line): 

# We insert blank lines to make up for the removed lines 

doc.extend(["\n"]*self.line_shift) 

self.line_shift = 0 

doc.append(line) 

unparsed_doc = True 

if not in_docstring and (not just_finished or self.start_finish_can_overlap): 

# to get line numbers in linked docstrings correct we 

# append a blank line to the doc list. 

doc.append("\n") 

if not line.strip(): 

continue 

if self.starting_docstring(line): 

in_docstring = True 

if self.linking: 

# If there's already a doctest, we overwrite it. 

if len(doctests) > 0: 

doctests.pop() 

if start is None: 

start = lineno 

doc = [] 

else: 

self.line_shift = 0 

start = lineno 

doc = [] 

# In ReST files we can end the file without decreasing the indentation level. 

if unparsed_doc: 

self._process_doc(doctests, doc, namespace, start) 

 

extras = dict(tab=not tab_okay and tab_locations, 

line_number=contains_line_number, 

optionals=self.parser.optionals) 

if self.options.randorder is not None and self.options.randorder is not False: 

# we want to randomize even when self.randorder = 0 

random.seed(self.options.randorder) 

randomized = [] 

while len(doctests) > 0: 

i = random.randint(0, len(doctests)-1) 

randomized.append(doctests.pop(i)) 

return randomized, extras 

else: 

return doctests, extras 

 

class StringDocTestSource(DocTestSource): 

r""" 

This class creates doctests from a string. 

 

INPUT: 

 

- ``basename`` -- string such as 'sage.doctests.sources', going 

into the names of created doctests and examples. 

 

- ``source`` -- a string, giving the source code to be parsed for 

doctests. 

 

- ``options`` -- a :class:`sage.doctest.control.DocTestDefaults` 

or equivalent. 

 

- ``printpath`` -- a string, to be used in place of a filename 

when doctest failures are displayed. 

 

- ``lineno_shift`` -- an integer (default: 0) by which to shift 

the line numbers of all doctests defined in this string. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import StringDocTestSource, PythonSource 

sage: from sage.structure.dynamic_class import dynamic_class 

sage: s = "'''\n sage: 2 + 2\n 4\n'''" 

sage: PythonStringSource = dynamic_class('PythonStringSource',(StringDocTestSource, PythonSource)) 

sage: PSS = PythonStringSource('<runtime>', s, DocTestDefaults(), 'runtime') 

sage: dt, extras = PSS.create_doctests({}) 

sage: len(dt) 

1 

sage: extras['tab'] 

[] 

sage: extras['line_number'] 

False 

 

sage: s = "'''\n\tsage: 2 + 2\n\t4\n'''" 

sage: PSS = PythonStringSource('<runtime>', s, DocTestDefaults(), 'runtime') 

sage: dt, extras = PSS.create_doctests({}) 

sage: extras['tab'] 

['2', '3'] 

 

sage: s = "'''\n sage: import warnings; warnings.warn('foo')\n doctest:1: UserWarning: foo \n'''" 

sage: PSS = PythonStringSource('<runtime>', s, DocTestDefaults(), 'runtime') 

sage: dt, extras = PSS.create_doctests({}) 

sage: extras['line_number'] 

True 

""" 

def __init__(self, basename, source, options, printpath, lineno_shift=0): 

r""" 

Initialization 

 

TESTS:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import StringDocTestSource, PythonSource 

sage: from sage.structure.dynamic_class import dynamic_class 

sage: s = "'''\n sage: 2 + 2\n 4\n'''" 

sage: PythonStringSource = dynamic_class('PythonStringSource',(StringDocTestSource, PythonSource)) 

sage: PSS = PythonStringSource('<runtime>', s, DocTestDefaults(), 'runtime') 

sage: TestSuite(PSS).run() 

""" 

self.qualified_name = NestedName(basename) 

self.printpath = printpath 

self.source = source 

self.lineno_shift = lineno_shift 

DocTestSource.__init__(self, options) 

 

def __iter__(self): 

""" 

Iterating over this source yields pairs ``(lineno, line)``. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import StringDocTestSource, PythonSource 

sage: from sage.structure.dynamic_class import dynamic_class 

sage: s = "'''\n sage: 2 + 2\n 4\n'''" 

sage: PythonStringSource = dynamic_class('PythonStringSource',(StringDocTestSource, PythonSource)) 

sage: PSS = PythonStringSource('<runtime>', s, DocTestDefaults(), 'runtime') 

sage: for n, line in PSS: 

....: print("{} {}".format(n, line)) 

0 ''' 

1 sage: 2 + 2 

2 4 

3 ''' 

""" 

for lineno, line in enumerate(self.source.split('\n')): 

yield lineno + self.lineno_shift, line + '\n' 

 

def create_doctests(self, namespace): 

r""" 

Creates doctests from this string. 

 

INPUT: 

 

- ``namespace`` -- a dictionary or :class:`sage.doctest.util.RecordingDict`. 

 

OUTPUT: 

 

- ``doctests`` -- a list of doctests defined by this string 

 

- ``tab_locations`` -- either False or a list of linenumbers 

on which tabs appear. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import StringDocTestSource, PythonSource 

sage: from sage.structure.dynamic_class import dynamic_class 

sage: s = "'''\n sage: 2 + 2\n 4\n'''" 

sage: PythonStringSource = dynamic_class('PythonStringSource',(StringDocTestSource, PythonSource)) 

sage: PSS = PythonStringSource('<runtime>', s, DocTestDefaults(), 'runtime') 

sage: dt, tabs = PSS.create_doctests({}) 

sage: for t in dt: 

....: print("{} {}".format(t.name, t.examples[0].sage_source)) 

<runtime> 2 + 2 

""" 

return self._create_doctests(namespace) 

 

 

class FileDocTestSource(DocTestSource): 

""" 

This class creates doctests from a file. 

 

INPUT: 

 

- ``path`` -- string, the filename 

 

- ``options`` -- a :class:`sage.doctest.control.DocTestDefaults` 

instance or equivalent. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: from sage.env import SAGE_SRC 

sage: import os 

sage: filename = os.path.join(SAGE_SRC,'sage','doctest','sources.py') 

sage: FDS = FileDocTestSource(filename,DocTestDefaults()) 

sage: FDS.basename 

'sage.doctest.sources' 

 

TESTS:: 

 

sage: TestSuite(FDS).run() 

""" 

def __init__(self, path, options): 

""" 

Initialization. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: from sage.env import SAGE_SRC 

sage: import os 

sage: filename = os.path.join(SAGE_SRC,'sage','doctest','sources.py') 

sage: FDS = FileDocTestSource(filename,DocTestDefaults(randorder=0)) 

sage: FDS.options.randorder 

0 

""" 

self.path = path 

DocTestSource.__init__(self, options) 

base, ext = os.path.splitext(path) 

if ext in ('.py', '.pyx', '.pxd', '.pxi', '.sage', '.spyx'): 

self.__class__ = dynamic_class('PythonFileSource',(FileDocTestSource,PythonSource)) 

self.encoding = "utf-8" 

elif ext == '.tex': 

self.__class__ = dynamic_class('TexFileSource',(FileDocTestSource,TexSource)) 

self.encoding = "utf-8" 

elif ext == '.rst': 

self.__class__ = dynamic_class('RestFileSource',(FileDocTestSource,RestSource)) 

self.encoding = "utf-8" 

else: 

raise ValueError("unknown file extension %r"%ext) 

 

def __iter__(self): 

r""" 

Iterating over this source yields pairs ``(lineno, line)``. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: filename = tmp_filename(ext=".py") 

sage: s = "'''\n sage: 2 + 2\n 4\n'''" 

sage: with open(filename, 'w') as f: 

....: _ = f.write(s) 

sage: FDS = FileDocTestSource(filename, DocTestDefaults()) 

sage: for n, line in FDS: 

....: print("{} {}".format(n, line)) 

0 ''' 

1 sage: 2 + 2 

2 4 

3 ''' 

 

The encoding is "utf-8" by default:: 

 

sage: FDS.encoding 

'utf-8' 

 

We create a file with a Latin-1 encoding without declaring it:: 

 

sage: s = b"'''\nRegardons le polyn\xF4me...\n'''\n" 

sage: with open(filename, 'wb') as f: 

....: _ = f.write(s) 

sage: FDS = FileDocTestSource(filename, DocTestDefaults()) 

sage: L = list(FDS) 

Traceback (most recent call last): 

... 

UnicodeDecodeError: 'utf...8' codec can't decode byte 0xf4 in position 18: invalid continuation byte 

 

This works if we add a PEP 0263 encoding declaration:: 

 

sage: s = b"#!/usr/bin/env python\n# -*- coding: latin-1 -*-\n" + s 

sage: with open(filename, 'wb') as f: 

....: _ = f.write(s) 

sage: FDS = FileDocTestSource(filename, DocTestDefaults()) 

sage: L = list(FDS) 

sage: FDS.encoding 

'latin-1' 

""" 

with open(self.path, 'rb') as source: 

for lineno, line in enumerate(source): 

if lineno < 2: 

match = pep_0263.search(line) 

if match: 

self.encoding = bytes_to_str(match.group(1), 'ascii') 

yield lineno, line.decode(self.encoding) 

 

@lazy_attribute 

def printpath(self): 

""" 

Whether the path is printed absolutely or relatively depends on an option. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: from sage.env import SAGE_SRC 

sage: import os 

sage: root = os.path.realpath(os.path.join(SAGE_SRC,'sage')) 

sage: filename = os.path.join(root,'doctest','sources.py') 

sage: cwd = os.getcwd() 

sage: os.chdir(root) 

sage: FDS = FileDocTestSource(filename,DocTestDefaults(randorder=0,abspath=False)) 

sage: FDS.printpath 

'doctest/sources.py' 

sage: FDS = FileDocTestSource(filename,DocTestDefaults(randorder=0,abspath=True)) 

sage: FDS.printpath 

'.../sage/doctest/sources.py' 

sage: os.chdir(cwd) 

""" 

if self.options.abspath: 

return os.path.abspath(self.path) 

else: 

relpath = os.path.relpath(self.path) 

if relpath.startswith(".." + os.path.sep): 

return self.path 

else: 

return relpath 

 

@lazy_attribute 

def basename(self): 

""" 

The basename of this file source, e.g. sage.doctest.sources 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: from sage.env import SAGE_SRC 

sage: import os 

sage: filename = os.path.join(SAGE_SRC,'sage','rings','integer.pyx') 

sage: FDS = FileDocTestSource(filename,DocTestDefaults()) 

sage: FDS.basename 

'sage.rings.integer' 

""" 

return get_basename(self.path) 

 

@lazy_attribute 

def in_lib(self): 

""" 

Whether this file is part of a package (i.e. is in a directory 

containing an ``__init__.py`` file). 

 

Such files aren't loaded before running tests. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: from sage.env import SAGE_SRC 

sage: import os 

sage: filename = os.path.join(SAGE_SRC, 'sage', 'rings', 'integer.pyx') 

sage: FDS = FileDocTestSource(filename, DocTestDefaults()) 

sage: FDS.in_lib 

True 

sage: filename = os.path.join(SAGE_SRC, 'sage', 'doctest', 'tests', 'abort.rst') 

sage: FDS = FileDocTestSource(filename, DocTestDefaults()) 

sage: FDS.in_lib 

False 

 

You can override the default:: 

 

sage: FDS = FileDocTestSource("hello_world.py",DocTestDefaults()) 

sage: FDS.in_lib 

False 

sage: FDS = FileDocTestSource("hello_world.py",DocTestDefaults(force_lib=True)) 

sage: FDS.in_lib 

True 

""" 

# We need an explicit bool() because is_package_dir() returns 

# 1/None instead of True/False. 

return bool(self.options.force_lib or 

is_package_dir(os.path.dirname(self.path))) 

 

def create_doctests(self, namespace): 

r""" 

Returns a list of doctests for this file. 

 

INPUT: 

 

- ``namespace`` -- a dictionary or :class:`sage.doctest.util.RecordingDict`. 

 

OUTPUT: 

 

- ``doctests`` -- a list of doctests defined in this file. 

 

- ``extras`` -- a dictionary 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: from sage.env import SAGE_SRC 

sage: import os 

sage: filename = os.path.join(SAGE_SRC,'sage','doctest','sources.py') 

sage: FDS = FileDocTestSource(filename,DocTestDefaults()) 

sage: doctests, extras = FDS.create_doctests(globals()) 

sage: len(doctests) 

41 

sage: extras['tab'] 

False 

 

We give a self referential example:: 

 

sage: doctests[18].name 

'sage.doctest.sources.FileDocTestSource.create_doctests' 

sage: doctests[18].examples[10].source 

u'doctests[Integer(18)].examples[Integer(10)].source\n' 

 

TESTS: 

 

We check that we correctly process results that depend on 32 

vs 64 bit architecture:: 

 

sage: import sys 

sage: bitness = '64' if sys.maxsize > (1 << 32) else '32' 

sage: n = -920390823904823094890238490238484; hash(n) > 0 

False # 32-bit 

True # 64-bit 

sage: ex = doctests[18].examples[13] 

sage: (bitness == '64' and ex.want == 'True \n') or (bitness == '32' and ex.want == 'False \n') 

True 

 

We check that lines starting with a # aren't doctested:: 

 

#sage: raise RuntimeError 

""" 

if not os.path.exists(self.path): 

import errno 

raise IOError(errno.ENOENT, "File does not exist", self.path) 

base, filename = os.path.split(self.path) 

_, ext = os.path.splitext(filename) 

if not self.in_lib and ext in ('.py', '.pyx', '.sage', '.spyx'): 

cwd = os.getcwd() 

if base: 

os.chdir(base) 

try: 

load(filename, namespace) # errors raised here will be caught in DocTestTask 

finally: 

os.chdir(cwd) 

self.qualified_name = NestedName(self.basename) 

return self._create_doctests(namespace) 

 

def _test_enough_doctests(self, check_extras=True, verbose=True): 

""" 

This function checks to see that the doctests are not getting 

unexpectedly skipped. It uses a different (and simpler) code 

path than the doctest creation functions, so there are a few 

files in Sage that it counts incorrectly. 

 

INPUT: 

 

- ``check_extras`` -- bool (default True), whether to check if 

doctests are created that don't correspond to either a 

``sage: `` or a ``>>> `` prompt. 

 

- ``verbose`` -- bool (default True), whether to print 

offending line numbers when there are missing or extra 

tests. 

 

TESTS:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: from sage.env import SAGE_SRC 

sage: cwd = os.getcwd() 

sage: os.chdir(SAGE_SRC) 

sage: import itertools 

sage: for path, dirs, files in itertools.chain(os.walk('sage'), os.walk('doc')): # long time 

....: path = os.path.relpath(path) 

....: dirs.sort(); files.sort() 

....: for F in files: 

....: _, ext = os.path.splitext(F) 

....: if ext in ('.py', '.pyx', '.pxd', '.pxi', '.sage', '.spyx', '.rst'): 

....: filename = os.path.join(path, F) 

....: FDS = FileDocTestSource(filename, DocTestDefaults(long=True,optional=True)) 

....: FDS._test_enough_doctests(verbose=False) 

There are 7 tests in sage/combinat/finite_state_machine.py that are not being run 

There are 3 unexpected tests being run in sage/doctest/parsing.py 

There are 1 unexpected tests being run in sage/doctest/reporting.py 

There are 3 tests in sage/rings/invariant_theory.py that are not being run 

sage: os.chdir(cwd) 

""" 

expected = [] 

rest = isinstance(self, RestSource) 

if rest: 

skipping = False 

in_block = False 

last_line = '' 

for lineno, line in self: 

if not line.strip(): 

continue 

if rest: 

if line.strip().startswith(".. nodoctest"): 

return 

# We need to track blocks in order to figure out whether we're skipping. 

if in_block: 

indent = whitespace.match(line).end() 

if indent <= starting_indent: 

in_block = False 

skipping = False 

if not in_block: 

m = double_colon.match(line) 

if m and not line.strip().startswith(".."): 

if ".. skip" in last_line: 

skipping = True 

in_block = True 

starting_indent = whitespace.match(line).end() 

last_line = line 

if (not rest or in_block) and sagestart.match(line) and not ((rest and skipping) or untested.search(line.lower())): 

expected.append(lineno+1) 

actual = [] 

tests, _ = self.create_doctests({}) 

for dt in tests: 

if len(dt.examples) > 0: 

for ex in dt.examples[:-1]: # the last entry is a sig_on_count() 

actual.append(dt.lineno + ex.lineno + 1) 

shortfall = sorted(list(set(expected).difference(set(actual)))) 

extras = sorted(list(set(actual).difference(set(expected)))) 

if len(actual) == len(expected): 

if len(shortfall) == 0: return 

dif = extras[0] - shortfall[0] 

for e, s in zip(extras[1:],shortfall[1:]): 

if dif != e - s: 

break 

else: 

print("There are %s tests in %s that are shifted by %s" % (len(shortfall), self.path, dif)) 

if verbose: 

print(" The correct line numbers are %s" % (", ".join([str(n) for n in shortfall]))) 

return 

elif len(actual) < len(expected): 

print("There are %s tests in %s that are not being run" % (len(expected) - len(actual), self.path)) 

elif check_extras: 

print("There are %s unexpected tests being run in %s" % (len(actual) - len(expected), self.path)) 

if verbose: 

if shortfall: 

print(" Tests on lines %s are not run" % (", ".join([str(n) for n in shortfall]))) 

if check_extras and extras: 

print(" Tests on lines %s seem extraneous" % (", ".join([str(n) for n in extras]))) 

 

class SourceLanguage: 

""" 

An abstract class for functions that depend on the programming language of a doctest source. 

 

Currently supported languages include Python, ReST and LaTeX. 

""" 

def parse_docstring(self, docstring, namespace, start): 

""" 

Return a list of doctest defined in this docstring. 

 

This function is called by :meth:`DocTestSource._process_doc`. 

The default implementation, defined here, is to use the 

:class:`sage.doctest.parsing.SageDocTestParser` attached to 

this source to get doctests from the docstring. 

 

INPUT: 

 

- ``docstring`` -- a string containing documentation and tests. 

 

- ``namespace`` -- a dictionary or :class:`sage.doctest.util.RecordingDict`. 

 

- ``start`` -- an integer, one less than the starting line number 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: from sage.doctest.parsing import SageDocTestParser 

sage: from sage.doctest.util import NestedName 

sage: from sage.env import SAGE_SRC 

sage: import os 

sage: filename = os.path.join(SAGE_SRC,'sage','doctest','util.py') 

sage: FDS = FileDocTestSource(filename,DocTestDefaults()) 

sage: doctests, _ = FDS.create_doctests({}) 

sage: for dt in doctests: 

....: FDS.qualified_name = dt.name 

....: dt.examples = dt.examples[:-1] # strip off the sig_on() test 

....: assert(FDS.parse_docstring(dt.docstring,{},dt.lineno-1)[0] == dt) 

""" 

return [self.parser.get_doctest(docstring, namespace, str(self.qualified_name), 

self.printpath, start + 1)] 

 

class PythonSource(SourceLanguage): 

""" 

This class defines the functions needed for the extraction of doctests from python sources. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: from sage.env import SAGE_SRC 

sage: import os 

sage: filename = os.path.join(SAGE_SRC,'sage','doctest','sources.py') 

sage: FDS = FileDocTestSource(filename,DocTestDefaults()) 

sage: type(FDS) 

<class 'sage.doctest.sources.PythonFileSource'> 

""" 

# The same line can't both start and end a docstring 

start_finish_can_overlap = False 

 

def _init(self): 

""" 

This function is called before creating doctests from a Python source. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: from sage.env import SAGE_SRC 

sage: import os 

sage: filename = os.path.join(SAGE_SRC,'sage','doctest','sources.py') 

sage: FDS = FileDocTestSource(filename,DocTestDefaults()) 

sage: FDS._init() 

sage: FDS.last_indent 

-1 

""" 

self.last_indent = -1 

self.last_line = None 

self.quotetype = None 

self.paren_count = 0 

self.bracket_count = 0 

self.curly_count = 0 

self.code_wrapping = False 

 

def _update_quotetype(self, line): 

r""" 

Updates the track of what kind of quoted string we're in. 

 

We need to track whether we're inside a triple quoted 

string, since a triple quoted string that starts a line 

could be the end of a string and thus not the beginning of a 

doctest (see sage.misc.sageinspect for an example). 

 

To do this tracking we need to track whether we're inside a 

string at all, since ''' inside a string doesn't start a 

triple quote (see the top of this file for an example). 

 

We also need to track parentheses and brackets, since we only 

want to update our record of last line and indentation level 

when the line is actually over. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: from sage.env import SAGE_SRC 

sage: import os 

sage: filename = os.path.join(SAGE_SRC,'sage','doctest','sources.py') 

sage: FDS = FileDocTestSource(filename,DocTestDefaults()) 

sage: FDS._init() 

sage: FDS._update_quotetype('\"\"\"'); print(" ".join(list(FDS.quotetype))) 

" " " 

sage: FDS._update_quotetype("'''"); print(" ".join(list(FDS.quotetype))) 

" " " 

sage: FDS._update_quotetype('\"\"\"'); print(FDS.quotetype) 

None 

sage: FDS._update_quotetype("triple_quotes = re.compile(\"\\s*[rRuU]*((''')|(\\\"\\\"\\\"))\")") 

sage: print(FDS.quotetype) 

None 

sage: FDS._update_quotetype("''' Single line triple quoted string \\''''") 

sage: print(FDS.quotetype) 

None 

sage: FDS._update_quotetype("' Lots of \\\\\\\\'") 

sage: print(FDS.quotetype) 

None 

""" 

def _update_parens(start,end=None): 

self.paren_count += line.count("(",start,end) - line.count(")",start,end) 

self.bracket_count += line.count("[",start,end) - line.count("]",start,end) 

self.curly_count += line.count("{",start,end) - line.count("}",start,end) 

pos = 0 

while pos < len(line): 

if self.quotetype is None: 

next_single = line.find("'",pos) 

next_double = line.find('"',pos) 

if next_single == -1 and next_double == -1: 

next_comment = line.find("#",pos) 

if next_comment == -1: 

_update_parens(pos) 

else: 

_update_parens(pos,next_comment) 

break 

elif next_single == -1: 

m = next_double 

elif next_double == -1: 

m = next_single 

else: 

m = min(next_single, next_double) 

next_comment = line.find('#',pos,m) 

if next_comment != -1: 

_update_parens(pos,next_comment) 

break 

_update_parens(pos,m) 

if m+2 < len(line) and line[m] == line[m+1] == line[m+2]: 

self.quotetype = line[m:m+3] 

pos = m+3 

else: 

self.quotetype = line[m] 

pos = m+1 

else: 

next = line.find(self.quotetype,pos) 

if next == -1: 

break 

elif next == 0 or line[next-1] != '\\': 

pos = next + len(self.quotetype) 

self.quotetype = None 

else: 

# We need to worry about the possibility that 

# there are an even number of backslashes before 

# the quote, in which case it is not escaped 

count = 1 

slashpos = next - 2 

while slashpos >= pos and line[slashpos] == '\\': 

count += 1 

slashpos -= 1 

if count % 2 == 0: 

pos = next + len(self.quotetype) 

self.quotetype = None 

else: 

# The possible ending quote was escaped. 

pos = next + 1 

 

def starting_docstring(self, line): 

""" 

Determines whether the input line starts a docstring. 

 

If the input line does start a docstring (a triple quote), 

then this function updates ``self.qualified_name``. 

 

INPUT: 

 

- ``line`` -- a string, one line of an input file 

 

OUTPUT: 

 

- either None or a Match object. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: from sage.doctest.util import NestedName 

sage: from sage.env import SAGE_SRC 

sage: import os 

sage: filename = os.path.join(SAGE_SRC,'sage','doctest','sources.py') 

sage: FDS = FileDocTestSource(filename,DocTestDefaults()) 

sage: FDS._init() 

sage: FDS.starting_docstring("r'''") 

<_sre.SRE_Match object...> 

sage: FDS.ending_docstring("'''") 

<_sre.SRE_Match object...> 

sage: FDS.qualified_name = NestedName(FDS.basename) 

sage: FDS.starting_docstring("class MyClass(object):") 

sage: FDS.starting_docstring(" def hello_world(self):") 

sage: FDS.starting_docstring(" '''") 

<_sre.SRE_Match object...> 

sage: FDS.qualified_name 

sage.doctest.sources.MyClass.hello_world 

sage: FDS.ending_docstring(" '''") 

<_sre.SRE_Match object...> 

sage: FDS.starting_docstring("class NewClass(object):") 

sage: FDS.starting_docstring(" '''") 

<_sre.SRE_Match object...> 

sage: FDS.ending_docstring(" '''") 

<_sre.SRE_Match object...> 

sage: FDS.qualified_name 

sage.doctest.sources.NewClass 

sage: FDS.starting_docstring("print(") 

sage: FDS.starting_docstring(" '''Not a docstring") 

sage: FDS.starting_docstring(" ''')") 

sage: FDS.starting_docstring("def foo():") 

sage: FDS.starting_docstring(" '''This is a docstring'''") 

<_sre.SRE_Match object...> 

""" 

indent = whitespace.match(line).end() 

quotematch = None 

if self.quotetype is None and not self.code_wrapping: 

# We're not inside a triple quote and not inside code like 

# print( 

# """Not a docstring 

# """) 

 

if line[indent] != '#' and (indent == 0 or indent > self.last_indent): 

quotematch = triple_quotes.match(line) 

# It would be nice to only run the name_regex when 

# quotematch wasn't None, but then we mishandle classes 

# that don't have a docstring. 

if not self.code_wrapping and self.last_indent >= 0 and indent > self.last_indent: 

name = name_regex.match(self.last_line) 

if name: 

name = name.groups()[0] 

self.qualified_name[indent] = name 

elif quotematch: 

self.qualified_name[indent] = '?' 

self._update_quotetype(line) 

if line[indent] != '#' and not self.code_wrapping: 

self.last_line, self.last_indent = line, indent 

self.code_wrapping = not (self.paren_count == self.bracket_count == self.curly_count == 0) 

return quotematch 

 

def ending_docstring(self, line): 

r""" 

Determines whether the input line ends a docstring. 

 

INPUT: 

 

- ``line`` -- a string, one line of an input file. 

 

OUTPUT: 

 

- an object that, when evaluated in a boolean context, gives 

True or False depending on whether the input line marks the 

end of a docstring. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: from sage.doctest.util import NestedName 

sage: from sage.env import SAGE_SRC 

sage: import os 

sage: filename = os.path.join(SAGE_SRC,'sage','doctest','sources.py') 

sage: FDS = FileDocTestSource(filename,DocTestDefaults()) 

sage: FDS._init() 

sage: FDS.quotetype = "'''" 

sage: FDS.ending_docstring("'''") 

<_sre.SRE_Match object...> 

sage: FDS.ending_docstring('\"\"\"') 

""" 

quotematch = triple_quotes.match(line) 

if quotematch is not None and quotematch.groups()[0] != self.quotetype: 

quotematch = None 

self._update_quotetype(line) 

return quotematch 

 

def _neutralize_doctests(self, reindent): 

r""" 

Returns a string containing the source of self, but with 

doctests modified so they aren't tested. 

 

This function is used in creating doctests for ReST files, 

since docstrings of Python functions defined inside verbatim 

blocks screw up Python's doctest parsing. 

 

INPUT: 

 

- ``reindent`` -- an integer, the number of spaces to indent 

the result. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import StringDocTestSource, PythonSource 

sage: from sage.structure.dynamic_class import dynamic_class 

sage: s = "'''\n sage: 2 + 2\n 4\n'''" 

sage: PythonStringSource = dynamic_class('PythonStringSource',(StringDocTestSource, PythonSource)) 

sage: PSS = PythonStringSource('<runtime>', s, DocTestDefaults(), 'runtime') 

sage: print(PSS._neutralize_doctests(0)) 

''' 

safe: 2 + 2 

4 

''' 

""" 

neutralized = [] 

in_docstring = False 

self._init() 

for lineno, line in self: 

if not line.strip(): 

neutralized.append(line) 

elif in_docstring: 

if self.ending_docstring(line): 

in_docstring = False 

neutralized.append(" "*reindent + find_prompt.sub(r"\1safe:\3",line)) 

else: 

if self.starting_docstring(line): 

in_docstring = True 

neutralized.append(" "*reindent + line) 

return "".join(neutralized) 

 

class TexSource(SourceLanguage): 

""" 

This class defines the functions needed for the extraction of 

doctests from a LaTeX source. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: filename = "sage_paper.tex" 

sage: FDS = FileDocTestSource(filename,DocTestDefaults()) 

sage: type(FDS) 

<class 'sage.doctest.sources.TexFileSource'> 

""" 

# The same line can't both start and end a docstring 

start_finish_can_overlap = False 

 

def _init(self): 

""" 

This function is called before creating doctests from a Tex file. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: filename = "sage_paper.tex" 

sage: FDS = FileDocTestSource(filename,DocTestDefaults()) 

sage: FDS._init() 

sage: FDS.skipping 

False 

""" 

self.skipping = False 

 

def starting_docstring(self, line): 

r""" 

Determines whether the input line starts a docstring. 

 

Docstring blocks in tex files are defined by verbatim or 

lstlisting environments, and can be linked together by adding 

%link immediately after the \end{verbatim} or \end{lstlisting}. 

 

Within a verbatim (or lstlisting) block, you can tell Sage not to 

process the rest of the block by including a %skip line. 

 

INPUT: 

 

- ``line`` -- a string, one line of an input file 

 

OUTPUT: 

 

- a boolean giving whether the input line marks the 

start of a docstring (verbatim block). 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: filename = "sage_paper.tex" 

sage: FDS = FileDocTestSource(filename,DocTestDefaults()) 

sage: FDS._init() 

 

We start docstrings with \begin{verbatim} or \begin{lstlisting}:: 

 

sage: FDS.starting_docstring(r"\begin{verbatim}") 

True 

sage: FDS.starting_docstring(r"\begin{lstlisting}") 

True 

sage: FDS.skipping 

False 

sage: FDS.ending_docstring("sage: 2+2") 

False 

sage: FDS.ending_docstring("4") 

False 

 

To start ignoring the rest of the verbatim block, use %skip:: 

 

sage: FDS.ending_docstring("%skip") 

True 

sage: FDS.skipping 

True 

sage: FDS.starting_docstring("sage: raise RuntimeError") 

False 

 

You can even pretend to start another verbatim block while skipping:: 

 

sage: FDS.starting_docstring(r"\begin{verbatim}") 

False 

sage: FDS.skipping 

True 

 

To stop skipping end the verbatim block:: 

 

sage: FDS.starting_docstring(r"\end{verbatim} %link") 

False 

sage: FDS.skipping 

False 

 

Linking works even when the block was ended while skipping:: 

 

sage: FDS.linking 

True 

sage: FDS.starting_docstring(r"\begin{verbatim}") 

True 

""" 

if self.skipping: 

if self.ending_docstring(line, check_skip=False): 

self.skipping = False 

return False 

return bool(begin_verb.match(line) or begin_lstli.match(line)) 

 

def ending_docstring(self, line, check_skip=True): 

r""" 

Determines whether the input line ends a docstring. 

 

Docstring blocks in tex files are defined by verbatim or 

lstlisting environments, and can be linked together by adding 

%link immediately after the \end{verbatim} or \end{lstlisting}. 

 

Within a verbatim (or lstlisting) block, you can tell Sage not to 

process the rest of the block by including a %skip line. 

 

INPUT: 

 

- ``line`` -- a string, one line of an input file 

 

- ``check_skip`` -- boolean (default True), used internally in starting_docstring. 

 

OUTPUT: 

 

- a boolean giving whether the input line marks the 

end of a docstring (verbatim block). 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: filename = "sage_paper.tex" 

sage: FDS = FileDocTestSource(filename,DocTestDefaults()) 

sage: FDS._init() 

sage: FDS.ending_docstring(r"\end{verbatim}") 

True 

sage: FDS.ending_docstring(r"\end{lstlisting}") 

True 

sage: FDS.linking 

False 

 

Use %link to link with the next verbatim block:: 

 

sage: FDS.ending_docstring(r"\end{verbatim}%link") 

True 

sage: FDS.linking 

True 

 

%skip also ends a docstring block:: 

 

sage: FDS.ending_docstring("%skip") 

True 

""" 

m = end_verb.match(line) 

if m: 

if m.groups()[0]: 

self.linking = True 

else: 

self.linking = False 

return True 

m = end_lstli.match(line) 

if m: 

if m.groups()[0]: 

self.linking = True 

else: 

self.linking = False 

return True 

if check_skip and skip.match(line): 

self.skipping = True 

return True 

return False 

 

 

class RestSource(SourceLanguage): 

""" 

This class defines the functions needed for the extraction of 

doctests from ReST sources. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: filename = "sage_doc.rst" 

sage: FDS = FileDocTestSource(filename,DocTestDefaults()) 

sage: type(FDS) 

<class 'sage.doctest.sources.RestFileSource'> 

""" 

# The same line can both start and end a docstring 

start_finish_can_overlap = True 

 

def _init(self): 

""" 

This function is called before creating doctests from a ReST file. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: filename = "sage_doc.rst" 

sage: FDS = FileDocTestSource(filename,DocTestDefaults()) 

sage: FDS._init() 

sage: FDS.link_all 

False 

""" 

self.link_all = False 

self.last_line = "" 

self.last_indent = -1 

self.first_line = False 

self.skipping = False 

 

def starting_docstring(self, line): 

""" 

A line ending with a double quote starts a verbatim block in a ReST file. 

 

This function also determines whether the docstring block 

should be joined with the previous one, or should be skipped. 

 

INPUT: 

 

- ``line`` -- a string, one line of an input file 

 

OUTPUT: 

 

- either None or a Match object. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: filename = "sage_doc.rst" 

sage: FDS = FileDocTestSource(filename,DocTestDefaults()) 

sage: FDS._init() 

sage: FDS.starting_docstring("Hello world::") 

True 

sage: FDS.ending_docstring(" sage: 2 + 2") 

False 

sage: FDS.ending_docstring(" 4") 

False 

sage: FDS.ending_docstring("We are now done") 

True 

sage: FDS.starting_docstring(".. link") 

sage: FDS.starting_docstring("::") 

True 

sage: FDS.linking 

True 

""" 

if link_all.match(line): 

self.link_all = True 

if self.skipping: 

end_block = self.ending_docstring(line) 

if end_block: 

self.skipping = False 

else: 

return False 

m = double_colon.match(line) 

starting = m and not line.strip().startswith(".. ") 

if starting: 

self.linking = self.link_all or '.. link' in self.last_line 

self.first_line = True 

indent = len(m.groups()[0]) 

if '.. skip' in self.last_line: 

self.skipping = True 

starting = False 

else: 

indent = self.last_indent 

self.last_line, self.last_indent = line, indent 

return starting 

 

def ending_docstring(self, line): 

""" 

When the indentation level drops below the initial level the 

block ends. 

 

INPUT: 

 

- ``line`` -- a string, one line of an input file 

 

OUTPUT: 

 

- a boolean, whether the verbatim block is ending. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: filename = "sage_doc.rst" 

sage: FDS = FileDocTestSource(filename,DocTestDefaults()) 

sage: FDS._init() 

sage: FDS.starting_docstring("Hello world::") 

True 

sage: FDS.ending_docstring(" sage: 2 + 2") 

False 

sage: FDS.ending_docstring(" 4") 

False 

sage: FDS.ending_docstring("We are now done") 

True 

""" 

if not line.strip(): 

return False 

indent = whitespace.match(line).end() 

if self.first_line: 

self.first_line = False 

if indent <= self.last_indent: 

# We didn't indent at all 

return True 

self.last_indent = indent 

return indent < self.last_indent 

 

def parse_docstring(self, docstring, namespace, start): 

r""" 

Return a list of doctest defined in this docstring. 

 

Code blocks in a REST file can contain python functions with 

their own docstrings in addition to in-line doctests. We want 

to include the tests from these inner docstrings, but Python's 

doctesting module has a problem if we just pass on the whole 

block, since it expects to get just a docstring, not the 

Python code as well. 

 

Our solution is to create a new doctest source from this code 

block and append the doctests created from that source. We 

then replace the occurrences of "sage:" and ">>>" occurring 

inside a triple quote with "safe:" so that the doctest module 

doesn't treat them as tests. 

 

EXAMPLES:: 

 

sage: from sage.doctest.control import DocTestDefaults 

sage: from sage.doctest.sources import FileDocTestSource 

sage: from sage.doctest.parsing import SageDocTestParser 

sage: from sage.doctest.util import NestedName 

sage: filename = "sage_doc.rst" 

sage: FDS = FileDocTestSource(filename,DocTestDefaults()) 

sage: FDS.parser = SageDocTestParser(False, set(['sage'])) 

sage: FDS.qualified_name = NestedName('sage_doc') 

sage: s = "Some text::\n\n def example_python_function(a, \ 

....: b):\n '''\n Brief description \ 

....: of function.\n\n EXAMPLES::\n\n \ 

....: sage: test1()\n sage: test2()\n \ 

....: '''\n return a + b\n\n sage: test3()\n\nMore \ 

....: ReST documentation." 

sage: tests = FDS.parse_docstring(s, {}, 100) 

sage: len(tests) 

2 

sage: for ex in tests[0].examples: 

....: print(ex.sage_source) 

test3() 

sage: for ex in tests[1].examples: 

....: print(ex.sage_source) 

test1() 

test2() 

sig_on_count() # check sig_on/off pairings (virtual doctest) 

""" 

PythonStringSource = dynamic_class("sage.doctest.sources.PythonStringSource", 

(StringDocTestSource, PythonSource)) 

min_indent = self.parser._min_indent(docstring) 

pysource = '\n'.join([l[min_indent:] for l in docstring.split('\n')]) 

inner_source = PythonStringSource(self.basename, pysource, 

self.options, 

self.printpath, lineno_shift=start+1) 

inner_doctests, _ = inner_source._create_doctests(namespace, True) 

safe_docstring = inner_source._neutralize_doctests(min_indent) 

outer_doctest = self.parser.get_doctest(safe_docstring, namespace, 

str(self.qualified_name), 

self.printpath, start + 1) 

return [outer_doctest] + inner_doctests 

 

class DictAsObject(dict): 

""" 

A simple subclass of dict that inserts the items from the initializing dictionary into attributes. 

 

EXAMPLES:: 

 

sage: from sage.doctest.sources import DictAsObject 

sage: D = DictAsObject({'a':2}) 

sage: D.a 

2 

""" 

def __init__(self, attrs): 

""" 

Initialization. 

 

INPUT: 

 

- ``attrs`` -- a dictionary. 

 

EXAMPLES:: 

 

sage: from sage.doctest.sources import DictAsObject 

sage: D = DictAsObject({'a':2}) 

sage: D.a == D['a'] 

True 

sage: D.a 

2 

""" 

super(DictAsObject, self).__init__(attrs) 

self.__dict__.update(attrs) 

 

def __setitem__(self, ky, val): 

""" 

We preserve the ability to access entries through either the 

dictionary or attribute interfaces. 

 

EXAMPLES:: 

 

sage: from sage.doctest.sources import DictAsObject 

sage: D = DictAsObject({}) 

sage: D['a'] = 2 

sage: D.a 

2 

""" 

super(DictAsObject, self).__setitem__(ky, val) 

try: 

super(DictAsObject, self).__setattr__(ky, val) 

except TypeError: 

pass 

 

def __setattr__(self, ky, val): 

""" 

We preserve the ability to access entries through either the 

dictionary or attribute interfaces. 

 

EXAMPLES:: 

 

sage: from sage.doctest.sources import DictAsObject 

sage: D = DictAsObject({}) 

sage: D.a = 2 

sage: D['a'] 

2 

""" 

super(DictAsObject, self).__setitem__(ky, val) 

super(DictAsObject, self).__setattr__(ky, val)