繁体   English   中英

python open()vs gzip.open()和文件模式

[英]python open() vs gzip.open() and file mode

为什么使用官方gzip模块中的 open()gzip.open()时文件模式会有所不同?

Linux上的Python 2.7。

在已打开的文件句柄上使用GzipFile时, GzipFile发生同样的事情。

我以为它应该是透明的,那么为什么我看到数字模式而不是rb / wb

测试脚本

#!/usr/bin/env python
"""
Write one file to another, with optional gzip on both sides.

Usage:
    gzipcat.py <input file> <output file>

Examples:
    gzipcat.py /etc/passwd passwd.bak.gz
    gzipcat.py passwd.bak.gz passwd.bak
"""

import sys
import gzip

if len(sys.argv) < 3:
    sys.exit(__doc__)

ifn = sys.argv[1]
if ifn.endswith('.gz'):
    ifd = gzip.open(ifn, 'rb')
else:
    ifd = open(ifn, 'rb')

ofn = sys.argv[2]
if ofn.endswith('.gz'):
    ofd = gzip.open(ofn, 'wb')
else:
    ofd = open(ofn, 'wb')

ifm = getattr(ifd, 'mode', None)
ofm = getattr(ofd, 'mode', None)

print('input file mode: {}, output file mode: {}'.format(ifm, ofm))

for ifl in ifd:
    ofd.write(ifl)

测试脚本输出

$ python gzipcat.py /etc/passwd passwd.bak
input file mode: rb, output file mode: wb
$ python gzipcat.py /etc/passwd passwd.bak.gz
input file mode: rb, output file mode: 2
$ python gzipcat.py passwd.bak.gz passwd.txt
input file mode: 1, output file mode: wb
$ python gzipcat.py passwd.bak.gz passwd.txt.gz
input file mode: 1, output file mode: 2

次要问题:这背后是否有充分的理由,还是gzip模块中只是遗漏/未处理的情况?

背景

我的实际用例是使用Google BigQuery加载程序,该加载程序要求将模式设为rb才能用作数据源。 下面的回溯。 但是我在上面准备了最低限度的测试用例,以使这个问题更易读。

# python -c 'import etl; etl.job001()'
Starting job001.
Processing table: reviews.
Extracting reviews, time range [2018-04-07 17:01:38.172129+00:00, 2018-04-07 18:09:50.763283)
Extracted 24 rows to reviews.tmp.gz in 2 s (8 rows/s).
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "etl.py", line 920, in wf_dimension_tables
    ts_end=ts_end)
  File "etl.py", line 680, in map_table_delta
    rewrite=True
  File "etl.py", line 624, in bq_load_csv
    job_config=job_config)
  File "/usr/lib/python2.7/site-packages/google/cloud/bigquery/client.py", line 797, in load_table_from_file
    _check_mode(file_obj)
  File "/usr/lib/python2.7/site-packages/google/cloud/bigquery/client.py", line 1419, in _check_mode
    "Cannot upload files opened in text mode:  use "
ValueError: Cannot upload files opened in text mode:  use open(filename, mode='rb') or open(filename, mode='r+b')

这是使用文件句柄的bigquery API调用:

def bq_load_csv(dataset_id, table_id, fileobj):
    client = bigquery.Client()
    dataset_ref = client.dataset(dataset_id)
    table_ref = dataset_ref.table(table_id)
    job_config = bigquery.LoadJobConfig()
    job_config.source_format = 'text/csv'
    job_config.field_delimiter = ','
    job_config.skip_leading_rows = 0
    job_config.allow_quoted_newlines = True
    job_config.max_bad_records = 0
    job = client.load_table_from_file(
        fileobj,
        table_ref,
        job_config=job_config)
    res = job.result()  # Waits for job to complete
    return res

更新

此问题已在python bigquery客户端1.5.0中修复。 感谢@ a-queue提交了错误报告,也感谢确实修复了该错误的Google开发人员。

解决此问题的正确方法是在Python和Google Cloud Client Library中分别针对Python的问题跟踪器引发问题。

解决方法

您可以代替google.cloud.bigquery.client _check_mode函数来接受12 ,就像我在下面所做的那样。 我试过运行此代码,它可以工作:

import gzip
from google.cloud import bigquery

def _check_mode(stream):
    mode = getattr(stream, 'mode', None)

    if mode is not None and mode not in ('rb', 'r+b', 'rb+', 1, 2):
        raise ValueError(
            "Cannot upload files opened in text mode:  use "
            "open(filename, mode='rb') or open(filename, mode='r+b')")


bigquery.client._check_mode = _check_mode

#...

def bq_load_csv(dataset_id, table_id, fileobj):
    #...

说明

谷歌云,蟒蛇

跟踪显示,最后失败的是google/cloud/bigquery/client.py _check_mode函数:

if mode is not None and mode not in ('rb', 'r+b', 'rb+'):
    raise ValueError(
        "Cannot upload files opened in text mode:  use "
        "open(filename, mode='rb') or open(filename, mode='r+b')")

gzip.py

而在功能的gzip库__init__类的GzipFile你可以看到,可变mode传递给此功能,但没有分配给self.mode但用来指定一个整数

READ, WRITE = 1, 2 #line 18
...
class GzipFile(_compression.BaseStream):
...
def __init__(self, filename=None, mode=None,
    ...
    elif mode.startswith(('w', 'a', 'x')): #line 179
        self.mode = WRITE

根据责任,第18行在21年前进行了更改,第180 self.mode = Write 20年前更改了self.mode = Write

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM