更新时间:2023-04-11 GMT+08:00

进阶用法的样例代码

如果您已经熟悉了常用操作,同时熟悉MoXing Framework API文档以及常用的Python编码,您可以参考本章节使用MoXing Framework的一些进阶用法。

读取完毕后将文件关闭

当读取OBS文件时,实际调用的是HTTP连接读去网络流,注意要记得在读取完毕后将文件关闭。为了防止忘记文件关闭操作,推荐使用with语句,在with语句退出时会自动调用mox.file.File对象的close()方法:

1
2
3
import moxing as mox
with mox.file.File('obs://bucket_name/obs_file.txt', 'r') as f:
  data = f.readlines()

利用pandas读或写一个OBS文件

  • 利用pandas读一个OBS文件。
    1
    2
    3
    4
    import pandas as pd
    import moxing as mox
    with mox.file.File("obs://bucket_name/b.txt", "r") as f:
      csv = pd.read_csv(f)
    
  • 利用pandas写一个OBS文件。
    1
    2
    3
    4
    5
    import pandas as pd
    import moxing as mox
    df = pd.DataFrame({'col1': [1, 2], 'col2': [3, 4]})
    with mox.file.File("obs://bucket_name/b.txt", "w") as f:
      df.to_csv(f)
    

利用文件对象读取图片

使用opencv打开一张图片时,无法传入一个obs路径,需要利用文件对象读取,考虑以下代码是无法读取到该图片的。

1
2
import cv2
cv2.imread('obs://bucket_name/xxx.jpg', cv2.IMREAD_COLOR)

修改为如下代码:

1
2
3
4
import cv2
import numpy as np
import moxing as mox
img = cv2.imdecode(np.fromstring(mox.file.read('obs://bucket_name/xxx.jpg', binary=True), np.uint8), cv2.IMREAD_COLOR)

利用已有的接口先实现一个不被mox.file支持的接口

如果用户API中调用到了不被mox.file支持的接口,那么可以利用已有的接口先实现这个新接口,然后覆盖原API。如os.path.isfile不在支持的接口范围内,当用户调用mox.file.shift('os', 'mox')后,os.path.isfile调用的依然是Python的原生builtin方法,按如下代码将该方法覆盖:
1
2
3
4
5
6
7
8
9
import os
import moxing as mox

_origin_isfile = os.path.isfile

def _patch_isfile(path):
  return not mox.file.isdir(path)

setattr(os.path, 'isfile', _patch_isfile)

将一个不支持obs路径的API改造成支持OBS路径的API

pandas中对h5的文件读写to_hdfread_hdf既不支持OBS路径,也不支持输入一个文件对象,考虑以下代码会出现错误。

1
2
3
4
import pandas as pd
df = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]}, index=['a', 'b', 'c'])
df.to_hdf('obs://wolfros-net/hdftest.h5', key='df', mode='w')
pd.read_hdf('obs://wolfros-net/hdftest.h5')

通过重写pandas源码API的方式,将该API改造成支持OBS路径的形式。

  • 写h5到OBS = 写h5到本地缓存 + 上传本地缓存到OBS + 删除本地缓存
  • 从OBS读h5 = 下载h5到本地缓存 + 读取本地缓存 + 删除本地缓存

即将以下代码写在运行脚本的最前面,就能使运行过程中的to_hdfread_hdf支持OBS路径。

 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
import os
import moxing as mox
import pandas as pd
from pandas.io import pytables
from pandas.core.generic import NDFrame

to_hdf_origin = getattr(NDFrame, 'to_hdf')
read_hdf_origin = getattr(pytables, 'read_hdf')


def to_hdf_override(self, path_or_buf, key, **kwargs):
  tmp_dir = '/cache/hdf_tmp'
  file_name = os.path.basename(path_or_buf)
  mox.file.make_dirs(tmp_dir)
  local_file = os.path.join(tmp_dir, file_name)
  to_hdf_origin(self, local_file, key, **kwargs)
  mox.file.copy(local_file, path_or_buf)
  mox.file.remove(local_file)


def read_hdf_override(path_or_buf, key=None, mode='r', **kwargs):
  tmp_dir = '/cache/hdf_tmp'
  file_name = os.path.basename(path_or_buf)
  mox.file.make_dirs(tmp_dir)
  local_file = os.path.join(tmp_dir, file_name)
  mox.file.copy(path_or_buf, local_file)
  result = read_hdf_origin(local_file, key, mode, **kwargs)
  mox.file.remove(local_file)
  return result

setattr(NDFrame, 'to_hdf', to_hdf_override)
setattr(pytables, 'read_hdf', read_hdf_override)
setattr(pd, 'read_hdf', read_hdf_override)

利用moxing,使h5py.File支持OBS

 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
import os
import h5py
import numpy as np
import moxing as mox

h5py_File_class = h5py.File

class OBSFile(h5py_File_class):
  def __init__(self, name, *args, **kwargs):
    self._tmp_name = None
    self._target_name = name
    if name.startswith('obs://'):
      self._tmp_name = name.replace('/', '_')
      if mox.file.exists(name):
        mox.file.copy(name, os.path.join('cache', 'h5py_tmp', self._tmp_name))
      name = self._tmp_name

    super(OBSFile, self).__init__(name, *args, **kwargs)

  def close(self):
    if self._tmp_name:
      mox.file.copy(self._tmp_name, self._target_name)

    super(OBSFile, self).close()


setattr(h5py, 'File', OBSFile)

arr = np.random.randn(1000)
with h5py.File('obs://bucket/random.hdf5', 'r') as f:
  f.create_dataset("default", data=arr)

with h5py.File('obs://bucket/random.hdf5', 'r') as f:
  print(f.require_dataset("default", dtype=np.float32, shape=(1000,)))