Skip to content

Commit 42f9a4c

Browse files
author
Steve Canny
committed
api: eliminate Document constructor function
1 parent e586504 commit 42f9a4c

File tree

5 files changed

+94
-92
lines changed

5 files changed

+94
-92
lines changed

Makefile

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
1-
PYTHON = python
2-
BEHAVE = behave
3-
SETUP = $(PYTHON) ./setup.py
1+
BEHAVE = behave
2+
MAKE = make
3+
PYTHON = python
4+
SETUP = $(PYTHON) ./setup.py
45

5-
.PHONY: accept clean coverage readme register sdist test upload
6+
.PHONY: accept clean coverage docs readme register sdist test upload
67

78
help:
89
@echo "Please use \`make <target>' where <target> is one or more of"
910
@echo " accept run acceptance tests using behave"
1011
@echo " clean delete intermediate work product and start fresh"
1112
@echo " coverage run nosetests with coverage"
13+
@echo " docs generate documentation
1214
@echo " readme update README.html from README.rst"
1315
@echo " register update metadata (README.rst) on PyPI"
1416
@echo " test run tests using setup.py"
@@ -25,6 +27,9 @@ clean:
2527
coverage:
2628
py.test --cov-report term-missing --cov=docx tests/
2729

30+
docs:
31+
$(MAKE) -C docs clean html
32+
2833
readme:
2934
rst2html README.rst >README.html
3035
open README.html

docs/conf.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,6 @@
7979
8080
.. |Document| replace:: :class:`Document`
8181
82-
.. |_Document| replace:: :class:`_Document`
83-
8482
.. |docx| replace:: ``python-docx``
8583
8684
.. |Emu| replace:: :class:`Emu`

docx/api.py

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,33 +12,17 @@
1212
from docx.package import Package
1313

1414

15-
thisdir = os.path.split(__file__)[0]
16-
_default_docx_path = os.path.join(thisdir, 'templates', 'default.docx')
15+
_thisdir = os.path.split(__file__)[0]
16+
_default_docx_path = os.path.join(_thisdir, 'templates', 'default.docx')
1717

1818

19-
def Document(docx=None):
20-
"""
21-
Return a |_Document| instance loaded from *docx*, where *docx* can be
22-
either a path to a ``.docx`` file (a string) or a file-like object. If
23-
*docx* is missing or ``None``, the built-in default document "template"
24-
is loaded.
25-
"""
26-
if docx is None:
27-
docx = _default_docx_path
28-
pkg = Package.open(docx)
29-
document_part = pkg.main_document
30-
if document_part.content_type != CT.WML_DOCUMENT_MAIN:
31-
tmpl = "file '%s' is not a Word file, content type is '%s'"
32-
raise ValueError(tmpl % (docx, document_part.content_type))
33-
return _Document(pkg, document_part)
34-
35-
36-
class _Document(object):
19+
class Document(object):
3720
"""
3821
API class representing a Word document.
3922
"""
40-
def __init__(self, package, document_part):
41-
super(_Document, self).__init__()
23+
def __init__(self, docx=None):
24+
super(Document, self).__init__()
25+
document_part, package = self._open(docx)
4226
self._document_part = document_part
4327
self._package = package
4428

@@ -70,3 +54,19 @@ def save(self, file_):
7054
a file (a string) or a file-like object.
7155
"""
7256
self._package.save(file_)
57+
58+
@staticmethod
59+
def _open(docx):
60+
"""
61+
Return a (document_part, package) 2-tuple loaded from *docx*, where
62+
*docx* can be either a path to a ``.docx`` file (a string) or a
63+
file-like object. If *docx* is ``None``, the built-in default
64+
document "template" is loaded.
65+
"""
66+
docx = _default_docx_path if docx is None else docx
67+
package = Package.open(docx)
68+
document_part = package.main_document
69+
if document_part.content_type != CT.WML_DOCUMENT_MAIN:
70+
tmpl = "file '%s' is not a Word file, content type is '%s'"
71+
raise ValueError(tmpl % (docx, document_part.content_type))
72+
return document_part, package

docx/parts/document.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# encoding: utf-8
22

33
"""
4-
Document parts such as _Document, and closely related classes.
4+
|DocumentPart| and closely related objects
55
"""
66

77
from __future__ import (

tests/test_api.py

Lines changed: 62 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -6,72 +6,38 @@
66

77
import pytest
88

9-
from docx.api import Document, _Document
9+
from docx.api import Document
1010
from docx.opc.constants import CONTENT_TYPE as CT
1111
from docx.package import Package
1212
from docx.parts.document import DocumentPart, InlineShapes
1313

14-
from .unitutil import class_mock, instance_mock, var_mock
14+
from .unitutil import class_mock, instance_mock, method_mock, var_mock
1515

1616

1717
class DescribeDocument(object):
1818

19-
def it_opens_a_docx_file(self, open_fixture):
20-
docx_, Package_, _Document_, package, document_part = open_fixture
21-
_document = Document(docx_)
19+
def it_opens_a_docx_on_construction(self, init_fixture):
20+
docx_, open_ = init_fixture
21+
document = Document(docx_)
22+
open_.assert_called_once_with(docx_)
23+
assert isinstance(document, Document)
24+
25+
def it_can_open_a_docx_file(self, open_fixture):
26+
docx_, Package_, package_, document_part_ = open_fixture
27+
document_part, package = Document._open(docx_)
2228
Package_.open.assert_called_once_with(docx_)
23-
_Document_.assert_called_once_with(package, document_part)
24-
assert _document is _Document_.return_value
29+
assert document_part is document_part
30+
assert package is package_
2531

26-
def it_uses_default_if_no_file_provided(self, Package_, default_docx_):
27-
Document()
32+
def it_opens_default_template_if_no_file_provided(
33+
self, Package_, default_docx_):
34+
Document._open(None)
2835
Package_.open.assert_called_once_with(default_docx_)
2936

3037
def it_should_raise_if_not_a_Word_file(self, Package_, package_, docx_):
3138
package_.main_document.content_type = 'foobar'
3239
with pytest.raises(ValueError):
33-
Document(docx_)
34-
35-
# fixtures -------------------------------------------------------
36-
37-
@pytest.fixture
38-
def _Document_(self, request):
39-
return class_mock(request, 'docx.api._Document')
40-
41-
@pytest.fixture
42-
def default_docx_(self, request):
43-
return var_mock(request, 'docx.api._default_docx_path')
44-
45-
@pytest.fixture
46-
def document_part_(self, request):
47-
return instance_mock(
48-
request, DocumentPart, content_type=CT.WML_DOCUMENT_MAIN
49-
)
50-
51-
@pytest.fixture
52-
def docx_(self, request):
53-
return instance_mock(request, str)
54-
55-
@pytest.fixture
56-
def open_fixture(
57-
self, request, docx_, Package_, _Document_, package_,
58-
document_part_):
59-
return docx_, Package_, _Document_, package_, document_part_
60-
61-
@pytest.fixture
62-
def Package_(self, request, package_):
63-
Package_ = class_mock(request, 'docx.api.Package')
64-
Package_.open.return_value = package_
65-
return Package_
66-
67-
@pytest.fixture
68-
def package_(self, request, document_part_):
69-
package_ = instance_mock(request, Package)
70-
package_.main_document = document_part_
71-
return package_
72-
73-
74-
class Describe_Document(object):
40+
Document._open(docx_)
7541

7642
def it_provides_access_to_the_document_body(self, document):
7743
body = document.body
@@ -82,14 +48,14 @@ def it_provides_access_to_the_document_inline_shapes(self, document):
8248
assert body is document._document_part.inline_shapes
8349

8450
def it_can_add_an_inline_picture(self, add_picture_fixture):
85-
document, inline_shapes, image_path_or_stream_, picture_shape_ = (
51+
document, inline_shapes, image_path_or_stream_, inline_picture_ = (
8652
add_picture_fixture
8753
)
88-
picture_shape = document.add_inline_picture(image_path_or_stream_)
54+
inline_picture = document.add_inline_picture(image_path_or_stream_)
8955
inline_shapes.add_picture.assert_called_once_with(
9056
image_path_or_stream_
9157
)
92-
assert picture_shape is picture_shape_
58+
assert inline_picture is inline_picture_
9359

9460
def it_can_save_the_package(self, save_fixture):
9561
document, package_, file_ = save_fixture
@@ -99,28 +65,61 @@ def it_can_save_the_package(self, save_fixture):
9965
# fixtures -------------------------------------------------------
10066

10167
@pytest.fixture
102-
def add_picture_fixture(self, request, document_part_):
103-
document = _Document(None, document_part_)
68+
def add_picture_fixture(self, request, open_, document_part_):
69+
document = Document()
10470
inline_shapes = instance_mock(request, InlineShapes)
10571
document_part_.inline_shapes = inline_shapes
10672
image_path_ = instance_mock(request, str)
10773
picture_shape_ = inline_shapes.add_picture.return_value
10874
return document, inline_shapes, image_path_, picture_shape_
10975

11076
@pytest.fixture
111-
def document(self, request, package_, document_part_):
112-
return _Document(package_, document_part_)
77+
def default_docx_(self, request):
78+
return var_mock(request, 'docx.api._default_docx_path')
79+
80+
@pytest.fixture
81+
def document(self, open_):
82+
return Document()
11383

11484
@pytest.fixture
11585
def document_part_(self, request):
116-
return instance_mock(request, DocumentPart)
86+
return instance_mock(
87+
request, DocumentPart, content_type=CT.WML_DOCUMENT_MAIN
88+
)
89+
90+
@pytest.fixture
91+
def docx_(self, request):
92+
return instance_mock(request, str)
93+
94+
@pytest.fixture
95+
def init_fixture(self, docx_, open_):
96+
return docx_, open_
11797

11898
@pytest.fixture
119-
def package_(self, request):
120-
return instance_mock(request, Package)
99+
def open_(self, request, document_part_, package_):
100+
return method_mock(
101+
request, Document, '_open',
102+
return_value=(document_part_, package_)
103+
)
104+
105+
@pytest.fixture
106+
def open_fixture(self, docx_, Package_, package_, document_part_):
107+
return docx_, Package_, package_, document_part_
108+
109+
@pytest.fixture
110+
def Package_(self, request, package_):
111+
Package_ = class_mock(request, 'docx.api.Package')
112+
Package_.open.return_value = package_
113+
return Package_
114+
115+
@pytest.fixture
116+
def package_(self, request, document_part_):
117+
package_ = instance_mock(request, Package)
118+
package_.main_document = document_part_
119+
return package_
121120

122121
@pytest.fixture
123-
def save_fixture(self, request, package_):
122+
def save_fixture(self, request, open_, package_):
124123
file_ = instance_mock(request, str)
125-
document = _Document(package_, None)
124+
document = Document()
126125
return document, package_, file_

0 commit comments

Comments
 (0)