Skip to content
GitLab
Explore
Sign in
Register
Commits on Source (4)
New upstream version 1.0.16
· aa8c0f07
Bas Couwenberg
authored
Feb 05, 2019
aa8c0f07
Merge tag 'upstream/1.0.16'
· 21d87eab
Bas Couwenberg
authored
Feb 05, 2019
Upstream version 1.0.16
21d87eab
New upstream release.
· a95369e9
Bas Couwenberg
authored
Feb 05, 2019
a95369e9
Set distribution to unstable.
· 0be70014
Bas Couwenberg
authored
Feb 05, 2019
0be70014
Show whitespace changes
Inline
Side-by-side
CHANGES.txt
View file @
0be70014
Changes
=======
1.0.16 (2019-02-04)
-------------------
- A bug preventing GCPs from being created with new BufferedDatasetWriter
instances (#1600) has been fixed (#1610).
- A previously unreported bug preventing BufferedDatasetWriters from being
opened in r+ mode has been fixed.
- A regression in creating CRS objects from PROJ4 strings that include
"+wktext" (#1609) has been fixed.
- Regressions in str representations of empty CRS objects and the handling of
unreferenced datasets in rasterio._base have been fixed (#1616).
- GDAL seems to work best if GDAL_DATA is set as early as possible. Ideally it
is set when building the library or in the environment before importing
Rasterio, but for wheels we patch GDAL_DATA into os.environ when rasterio.env
is imported. This resolves #1611.
1.0.15 (2019-01-27)
-------------------
...
...
debian/changelog
View file @
0be70014
rasterio (1.0.16-1) unstable; urgency=medium
* Team upload.
* New upstream release.
-- Bas Couwenberg <sebastic@debian.org> Tue, 05 Feb 2019 15:34:38 +0100
rasterio (1.0.15-1) unstable; urgency=medium
* Team upload.
...
...
rasterio/__init__.py
View file @
0be70014
...
...
@@ -42,7 +42,7 @@ import rasterio.path
__all__
=
[
'
band
'
,
'
open
'
,
'
pad
'
,
'
Env
'
]
__version__
=
"
1.0.1
5
"
__version__
=
"
1.0.1
6
"
__gdal_version__
=
gdal_version
()
# Rasterio attaches NullHandler to the 'rasterio' logger and its
...
...
rasterio/_base.pyx
View file @
0be70014
...
...
@@ -267,7 +267,7 @@ cdef class DatasetBase(object):
if
wkt
:
return
CRS
.
from_wkt
(
wkt
)
else
:
return
CRS
()
return
None
def
read_crs
(
self
):
"""
Return the GDAL dataset
'
s stored CRS
"""
...
...
rasterio/_crs.pyx
View file @
0be70014
...
...
@@ -3,10 +3,10 @@
import
logging
import
rasterio._env
from
rasterio._err
import
CPLE_BaseError
,
CPLE_NotSupportedError
from
rasterio.compat
import
string_types
from
rasterio.errors
import
CRSError
from
rasterio.env
import
env_ctx_if_needed
from
rasterio._base
cimport
_osr_from_crs
as
osr_from_crs
from
rasterio._base
cimport
_safe_osr_release
...
...
@@ -175,10 +175,8 @@ cdef class _CRS(object):
try
:
exc_wrap_ogrerr
(
exc_wrap_int
(
OSRImportFromProj4
(
obj
.
_osr
,
<
const
char
*>
proj_b
)))
except
CPLE_BaseError
as
exc
:
raise
CRSError
(
"
The PROJ4 dict could not be understood. {}
"
.
format
(
exc
))
else
:
return
obj
...
...
@@ -247,13 +245,10 @@ cdef class _CRS(object):
try
:
errcode
=
exc_wrap_ogrerr
(
OSRImportFromWkt
(
obj
.
_osr
,
&
wkt_c
))
if
morph_from_esri_dialect
:
exc_wrap_ogrerr
(
OSRMorphFromESRI
(
obj
.
_osr
))
except
CPLE_BaseError
as
exc
:
raise
CRSError
(
"
The WKT could not be parsed. {}
"
.
format
(
exc
))
else
:
return
obj
...
...
@@ -414,12 +409,11 @@ _param_data = """
+vopt
+W
+westo
+wktext
+x_0 False easting
+y_0 False northing
+zone UTM zone
"""
with
env_ctx_if_needed
():
_lines
=
filter
(
lambda
x
:
len
(
x
)
>
1
,
_param_data
.
split
(
"
\n
"
))
all_proj_keys
=
list
(
set
(
line
.
split
()[
0
].
lstrip
(
"
+
"
).
strip
()
for
line
in
_lines
))
+
[
'
no_mayo
'
]
all_proj_keys
=
list
(
set
(
line
.
split
()[
0
].
lstrip
(
"
+
"
).
strip
()
for
line
in
_lines
))
+
[
'
no_mayo
'
]
rasterio/_io.pyx
View file @
0be70014
...
...
@@ -117,16 +117,21 @@ cdef int io_auto(data, GDALRasterBandH band, bint write, int resampling=0) excep
cdef
float
height
=
data
.
shape
[
-
2
]
cdef
float
width
=
data
.
shape
[
-
1
]
try
:
if
ndims
==
2
:
return
io_band
(
band
,
write
,
0.0
,
0.0
,
width
,
height
,
data
,
resampling
=
resampling
)
return
io_band
(
band
,
write
,
0.0
,
0.0
,
width
,
height
,
data
,
resampling
=
resampling
)
elif
ndims
==
3
:
indexes
=
np
.
arange
(
1
,
data
.
shape
[
0
]
+
1
,
dtype
=
'
intp
'
)
return
io_multi_band
(
band
,
write
,
0.0
,
0.0
,
width
,
height
,
data
,
indexes
,
resampling
=
resampling
)
return
io_multi_band
(
band
,
write
,
0.0
,
0.0
,
width
,
height
,
data
,
indexes
,
resampling
=
resampling
)
else
:
raise
ValueError
(
"
Specified data must have 2 or 3 dimensions
"
)
except
CPLE_BaseError
as
cplerr
:
raise
RasterioIOError
(
str
(
cplerr
))
cdef
class
DatasetReaderBase
(
DatasetBase
):
...
...
@@ -673,6 +678,8 @@ cdef class DatasetReaderBase(DatasetBase):
indexes_arr
=
np
.
array
(
indexes
,
dtype
=
'
intp
'
)
indexes_count
=
<
int
>
indexes_arr
.
shape
[
0
]
try
:
if
masks
:
# Warn if nodata attribute is shadowing an alpha band.
if
self
.
count
==
4
and
self
.
colorinterp
[
3
]
==
ColorInterp
.
alpha
:
...
...
@@ -680,18 +687,13 @@ cdef class DatasetReaderBase(DatasetBase):
if
MaskFlags
.
nodata
in
flags
:
warnings
.
warn
(
NodataShadowWarning
())
retval
=
io_multi_mask
(
self
.
_hds
,
0
,
xoff
,
yoff
,
width
,
height
,
out
,
indexes_arr
,
resampling
=
resampling
)
io_multi_mask
(
self
.
_hds
,
0
,
xoff
,
yoff
,
width
,
height
,
out
,
indexes_arr
,
resampling
=
resampling
)
else
:
retval
=
io_multi_band
(
self
.
_hds
,
0
,
xoff
,
yoff
,
width
,
height
,
out
,
indexes_arr
,
resampling
=
resampling
)
io_multi_band
(
self
.
_hds
,
0
,
xoff
,
yoff
,
width
,
height
,
out
,
indexes_arr
,
resampling
=
resampling
)
if
retval
in
(
1
,
2
,
3
):
raise
IOError
(
"
Read or write failed
"
)
elif
retval
==
4
:
raise
ValueError
(
"
NULL band
"
)
except
CPLE_BaseError
as
cplerr
:
raise
RasterioIOError
(
"
Read or write failed. {}
"
.
format
(
cplerr
))
return
out
...
...
@@ -1354,13 +1356,11 @@ cdef class DatasetWriterBase(DatasetReaderBase):
indexes_arr
=
np
.
array
(
indexes
,
dtype
=
'
intp
'
)
indexes_count
=
<
int
>
indexes_arr
.
shape
[
0
]
retval
=
io_multi_band
(
self
.
_hds
,
1
,
xoff
,
yoff
,
width
,
height
,
src
,
indexes_arr
)
if
retval
in
(
1
,
2
,
3
)
:
raise
IOError
(
"
Read or write failed
"
)
e
lif
retval
==
4
:
raise
ValueError
(
"
NULL band
"
)
try
:
io_multi_band
(
self
.
_hds
,
1
,
xoff
,
yoff
,
width
,
height
,
src
,
indexes_arr
)
e
xcept
CPLE_BaseError
as
cplerr
:
raise
RasterioIOError
(
"
Read or write failed. {}
"
.
format
(
cplerr
)
)
def
write_band
(
self
,
bidx
,
src
,
window
=
None
):
"""
Write the src array into the `bidx` band.
...
...
@@ -1540,15 +1540,19 @@ cdef class DatasetWriterBase(DatasetReaderBase):
width
=
self
.
width
height
=
self
.
height
try
:
if
mask_array
is
True
:
GDALFillRaster
(
mask
,
255
,
0
)
elif
mask_array
is
False
:
GDALFillRaster
(
mask
,
0
,
0
)
elif
mask_array
.
dtype
==
np
.
bool
:
array
=
255
*
mask_array
.
astype
(
np
.
uint8
)
retval
=
io_band
(
mask
,
1
,
xoff
,
yoff
,
width
,
height
,
array
)
io_band
(
mask
,
1
,
xoff
,
yoff
,
width
,
height
,
array
)
else
:
retval
=
io_band
(
mask
,
1
,
xoff
,
yoff
,
width
,
height
,
mask_array
)
io_band
(
mask
,
1
,
xoff
,
yoff
,
width
,
height
,
mask_array
)
except
CPLE_BaseError
as
cplerr
:
raise
RasterioIOError
(
"
Read or write failed. {}
"
.
format
(
cplerr
))
def
build_overviews
(
self
,
factors
,
resampling
=
Resampling
.
nearest
):
"""
Build overviews at one or more decimation factors for all
...
...
@@ -1782,22 +1786,32 @@ cdef class InMemoryRaster:
self
.
_hds
=
NULL
def
read
(
self
):
if
self
.
_image
is
None
:
raise
IOError
(
"
You need to write data before you can read the data.
"
)
raise
Rasterio
IOError
(
"
You need to write data before you can read the data.
"
)
try
:
if
self
.
_image
.
ndim
==
2
:
exc_wrap_int
(
io_auto
(
self
.
_image
,
self
.
band
(
1
),
False
)
)
io_auto
(
self
.
_image
,
self
.
band
(
1
),
False
)
else
:
exc_wrap_int
(
io_auto
(
self
.
_image
,
self
.
_hds
,
False
))
io_auto
(
self
.
_image
,
self
.
_hds
,
False
)
except
CPLE_BaseError
as
cplerr
:
raise
RasterioIOError
(
"
Read or write failed. {}
"
.
format
(
cplerr
))
return
self
.
_image
def
write
(
self
,
np
.
ndarray
image
):
self
.
_image
=
image
try
:
if
image
.
ndim
==
2
:
exc_wrap_int
(
io_auto
(
self
.
_image
,
self
.
band
(
1
),
True
)
)
io_auto
(
self
.
_image
,
self
.
band
(
1
),
True
)
else
:
exc_wrap_int
(
io_auto
(
self
.
_image
,
self
.
_hds
,
True
)
)
io_auto
(
self
.
_image
,
self
.
_hds
,
True
)
except
CPLE_BaseError
as
cplerr
:
raise
RasterioIOError
(
"
Read or write failed. {}
"
.
format
(
cplerr
))
cdef
class
BufferedDatasetWriterBase
(
DatasetWriterBase
):
...
...
@@ -1956,10 +1970,11 @@ cdef class BufferedDatasetWriterBase(DatasetWriterBase):
self
.
write_transform
(
self
.
_transform
)
if
self
.
_crs
:
self
.
_set_crs
(
self
.
_crs
)
if
self
.
_gcps
:
self
.
_set_gcps
(
self
.
_gcps
,
self
.
_crs
)
if
self
.
_
init_
gcps
:
self
.
_set_gcps
(
self
.
_
init_
gcps
,
self
.
_crs
)
elif
self
.
mode
==
'
r+
'
:
fname
=
name_b
try
:
temp
=
exc_wrap_pointer
(
GDALOpenShared
(
fname
,
<
GDALAccess
>
0
))
except
Exception
as
exc
:
...
...
@@ -1969,7 +1984,7 @@ cdef class BufferedDatasetWriterBase(DatasetWriterBase):
GDALCreateCopy
(
memdrv
,
"
temp
"
,
temp
,
1
,
NULL
,
NULL
,
NULL
))
drv
=
GDALGetDatasetDriver
(
temp
)
self
.
driver
=
get_driver_name
(
drv
)
self
.
driver
=
get_driver_name
(
drv
)
.
decode
(
'
utf-8
'
)
GDALClose
(
temp
)
# Instead of calling _begin() we do the following.
...
...
rasterio/_shim1.pyx
View file @
0be70014
...
...
@@ -10,7 +10,7 @@ from rasterio import dtypes
from
rasterio.enums
import
Resampling
cimport
numpy
as
np
from
rasterio._err
cimport
exc_wrap_pointer
from
rasterio._err
cimport
exc_wrap_int
,
exc_wrap_pointer
from
rasterio.errors
import
GDALOptionNotImplementedError
...
...
@@ -88,7 +88,7 @@ cdef int io_band(
band
,
mode
,
xoff
,
yoff
,
xsize
,
ysize
,
buf
,
bufxsize
,
bufysize
,
buftype
,
bufpixelspace
,
buflinespace
)
return
retval
return
exc_wrap_int
(
retval
)
cdef
int
io_multi_band
(
...
...
@@ -121,17 +121,21 @@ cdef int io_multi_band(
cdef
int
xsize
=
<
int
>
width
cdef
int
ysize
=
<
int
>
height
with
nogil
:
bandmap
=
<
int
*>
CPLMalloc
(
count
*
sizeof
(
int
))
for
i
in
range
(
count
):
bandmap
[
i
]
=
<
int
>
indexes
[
i
]
try
:
with
nogil
:
retval
=
GDALDatasetRasterIO
(
hds
,
mode
,
xoff
,
yoff
,
xsize
,
ysize
,
buf
,
bufxsize
,
bufysize
,
buftype
,
count
,
bandmap
,
bufpixelspace
,
buflinespace
,
bufbandspace
)
CPLFree
(
bandmap
)
return
retval
return
exc_wrap_int
(
retval
)
finally
:
CPLFree
(
bandmap
)
cdef
int
io_multi_mask
(
...
...
@@ -183,4 +187,4 @@ cdef int io_multi_mask(
if
retval
:
break
return
retval
return
exc_wrap_int
(
retval
)
rasterio/crs.py
View file @
0be70014
...
...
@@ -71,7 +71,17 @@ class CRS(collections.Mapping):
if
'
init
'
in
data
:
data
[
'
init
'
]
=
data
[
'
init
'
].
replace
(
'
EPSG:
'
,
'
epsg:
'
)
proj
=
'
'
.
join
([
'
+{}={}
'
.
format
(
key
,
val
)
for
key
,
val
in
data
.
items
()])
proj_parts
=
[]
for
key
,
val
in
data
.
items
():
if
val
is
False
or
None
:
continue
elif
val
is
True
:
proj_parts
.
append
(
'
+{}
'
.
format
(
key
))
else
:
proj_parts
.
append
(
'
+{}={}
'
.
format
(
key
,
val
))
proj
=
'
'
.
join
(
proj_parts
)
self
.
_crs
=
_CRS
.
from_proj4
(
proj
)
else
:
...
...
@@ -157,11 +167,18 @@ class CRS(collections.Mapping):
dict
"""
if
self
.
_crs
is
None
:
raise
CRSError
(
"
Undefined CRS has no dict representation
"
)
else
:
epsg_code
=
self
.
to_epsg
()
if
epsg_code
:
return
{
'
init
'
:
'
epsg:{}
'
.
format
(
epsg_code
)}
else
:
try
:
return
self
.
_crs
.
to_dict
()
except
CRSError
:
return
{}
@property
def
data
(
self
):
...
...
rasterio/env.py
View file @
0be70014
...
...
@@ -8,7 +8,9 @@ import re
import
threading
import
warnings
from
rasterio._env
import
GDALEnv
,
get_gdal_config
,
set_gdal_config
from
rasterio._env
import
(
GDALEnv
,
get_gdal_config
,
set_gdal_config
,
GDALDataFinder
,
PROJDataFinder
)
from
rasterio.compat
import
string_types
,
getargspec
from
rasterio.errors
import
(
EnvError
,
GDALVersionError
,
RasterioDeprecationWarning
)
...
...
@@ -598,3 +600,22 @@ def require_gdal_version(version, param=None, values=None, is_max_version=False,
return
wrapper
return
decorator
# Patch the environment if needed, such as in the installed wheel case.
if
'
GDAL_DATA
'
not
in
os
.
environ
:
path
=
GDALDataFinder
().
search
()
if
path
:
os
.
environ
[
'
GDAL_DATA
'
]
=
path
log
.
debug
(
"
GDAL_DATA not found in environment, set to %r.
"
,
path
)
if
'
PROJ_LIB
'
not
in
os
.
environ
:
path
=
PROJDataFinder
().
search
()
if
path
:
os
.
environ
[
'
PROJ_LIB
'
]
=
path
log
.
debug
(
"
PROJ data not found in environment, set to %r.
"
,
path
)
rasterio/shim_rasterioex.pxi
View file @
0be70014
...
...
@@ -6,6 +6,8 @@ from rasterio.enums import Resampling
cimport
numpy
as
np
from
rasterio._err
cimport
exc_wrap_int
cdef
extern
from
"
cpl_progress.h
"
:
...
...
@@ -73,7 +75,7 @@ cdef int io_band(GDALRasterBandH band, int mode, float x0, float y0,
band
,
<
GDALRWFlag
>
mode
,
xoff
,
yoff
,
xsize
,
ysize
,
buf
,
bufxsize
,
bufysize
,
buftype
,
bufpixelspace
,
buflinespace
,
&
extras
)
return
retval
return
exc_wrap_int
(
retval
)
cdef
int
io_multi_band
(
GDALDatasetH
hds
,
int
mode
,
float
x0
,
float
y0
,
...
...
@@ -117,17 +119,21 @@ cdef int io_multi_band(GDALDatasetH hds, int mode, float x0, float y0,
extras
.
pfnProgress
=
NULL
extras
.
pProgressData
=
NULL
with
nogil
:
bandmap
=
<
int
*>
CPLMalloc
(
count
*
sizeof
(
int
))
for
i
in
range
(
count
):
bandmap
[
i
]
=
<
int
>
indexes
[
i
]
try
:
with
nogil
:
retval
=
GDALDatasetRasterIOEx
(
hds
,
<
GDALRWFlag
>
mode
,
xoff
,
yoff
,
xsize
,
ysize
,
buf
,
bufxsize
,
bufysize
,
buftype
,
count
,
bandmap
,
bufpixelspace
,
buflinespace
,
bufbandspace
,
&
extras
)
CPLFree
(
bandmap
)
return
retval
return
exc_wrap_int
(
retval
)
finally
:
CPLFree
(
bandmap
)
cdef
int
io_multi_mask
(
GDALDatasetH
hds
,
int
mode
,
float
x0
,
float
y0
,
...
...
@@ -187,7 +193,8 @@ cdef int io_multi_mask(GDALDatasetH hds, int mode, float x0, float y0,
retval
=
GDALRasterIOEx
(
hmask
,
<
GDALRWFlag
>
mode
,
xoff
,
yoff
,
xsize
,
ysize
,
buf
,
bufxsize
,
bufysize
,
<
GDALDataType
>
1
,
bufpixelspace
,
buflinespace
,
&
extras
)
if
retval
:
break
return
retval
return
exc_wrap_int
(
retval
)
tests/test_crs.py
View file @
0be70014
...
...
@@ -88,6 +88,12 @@ def test_read_esri_wkt():
}
def
test_read_no_crs
():
"""
crs of a dataset with no SRS is None
"""
with
rasterio
.
open
(
'
tests/data/389225main_sw_1965_1024.jpg
'
)
as
src
:
assert
src
.
crs
is
None
# Ensure that CRS sticks when we write a file.
@pytest.mark.gdalbin
def
test_write_3857
(
tmpdir
):
...
...
@@ -385,3 +391,38 @@ def test_implicit_proj_dict(projection_string):
def
test_capitalized_epsg_init
():
"""
Ensure that old behavior is preserved
"""
assert
CRS
(
init
=
'
EPSG:4326
'
).
to_epsg
()
==
4326
def
test_issue1609_wktext_a
():
"""
Check on fix of issue 1609
"""
src_proj
=
{
'
ellps
'
:
'
WGS84
'
,
'
proj
'
:
'
stere
'
,
'
lat_0
'
:
-
90.0
,
'
lon_0
'
:
0.0
,
'
x_0
'
:
0.0
,
'
y_0
'
:
0.0
,
'
lat_ts
'
:
-
70
,
'
no_defs
'
:
True
}
wkt
=
CRS
(
src_proj
).
wkt
assert
'
PROJECTION[
"
Polar_Stereographic
"
]
'
in
wkt
assert
'
PARAMETER[
"
latitude_of_origin
"
,-70]
'
in
wkt
def
test_issue1609_wktext_b
():
"""
Check on fix of issue 1609
"""
dst_proj
=
{
'
ellps
'
:
'
WGS84
'
,
'
h
'
:
9000000.0
,
'
lat_0
'
:
-
78.0
,
'
lon_0
'
:
0.0
,
'
proj
'
:
'
nsper
'
,
'
units
'
:
'
m
'
,
'
x_0
'
:
0
,
'
y_0
'
:
0
,
'
wktext
'
:
True
}
wkt
=
CRS
(
dst_proj
).
wkt
assert
'
+wktext
'
in
wkt
def
test_empty_crs_str
():
"""
str(CRS()) should be empty string
"""
assert
str
(
CRS
())
==
''
\ No newline at end of file
tests/test_env.py
View file @
0be70014
...
...
@@ -126,6 +126,7 @@ def test_ensure_env_decorator_sets_gdal_data(gdalenv, monkeypatch):
def
test_ensure_env_decorator_sets_gdal_data_prefix
(
gdalenv
,
monkeypatch
,
tmpdir
):
"""
ensure_env finds GDAL data under a prefix
"""
@ensure_env
def
f
():
return
getenv
()[
'
GDAL_DATA
'
]
...
...
tests/test_gcps.py
View file @
0be70014
"""
Tests of ground control points
"""
import
numpy
import
pytest
import
rasterio
...
...
@@ -79,6 +80,35 @@ def test_write_read_gcps(tmpdir):
assert
(
200.0
,
2000.0
,
0.0
)
==
(
point
.
x
,
point
.
y
,
point
.
z
)
def
test_write_read_gcps_buffereddatasetwriter
(
tmpdir
):
filename
=
str
(
tmpdir
.
join
(
'
test.jpg
'
))
gcps
=
[
GroundControlPoint
(
1
,
1
,
100.0
,
1000.0
,
z
=
0.0
)]
with
rasterio
.
open
(
filename
,
'
w
'
,
driver
=
'
JPEG
'
,
dtype
=
'
uint8
'
,
count
=
3
,
width
=
10
,
height
=
10
,
crs
=
'
epsg:4326
'
,
gcps
=
gcps
)
as
dst
:
dst
.
write
(
numpy
.
ones
((
3
,
10
,
10
),
dtype
=
'
uint8
'
))
with
rasterio
.
open
(
filename
,
'
r+
'
)
as
dst
:
gcps
,
crs
=
dst
.
gcps
assert
crs
.
to_epsg
()
==
4326
assert
len
(
gcps
)
==
1
point
=
gcps
[
0
]
assert
(
1
,
1
)
==
(
point
.
row
,
point
.
col
)
assert
(
100.0
,
1000.0
,
0.0
)
==
(
point
.
x
,
point
.
y
,
point
.
z
)
dst
.
gcps
=
[
GroundControlPoint
(
1
,
1
,
100.0
,
1000.0
,
z
=
0.0
),
GroundControlPoint
(
2
,
2
,
200.0
,
2000.0
,
z
=
0.0
)],
crs
gcps
,
crs
=
dst
.
gcps
assert
crs
.
to_epsg
()
==
4326
assert
len
(
gcps
)
==
2
point
=
gcps
[
1
]
assert
(
2
,
2
)
==
(
point
.
row
,
point
.
col
)
assert
(
200.0
,
2000.0
,
0.0
)
==
(
point
.
x
,
point
.
y
,
point
.
z
)
def
test_read_vrt_gcps
(
tmpdir
):
vrtfile
=
tmpdir
.
join
(
'
test.vrt
'
)
vrtfile
.
write
(
"""
...
...