files: Header section metadata
  =======================
  
  Tutorial
  --------
  
  One of the primary motivations in writing lasio was to be able to reliably
  parse LAS header sections. This is working well for LAS 1.2 and 2.0
  files, and partially for LAS 3.0 files. 
  
  .. note::
  
     lasio does not require LAS files to be strictly compliant with the standards,
     and you should not expect lasio to raise an exception or error for files which
     are clearly not conforming to the standards. The goal of lasio is to parse
     metadata and data quietly, not to fail unnecessarily.
  
  .. code-block:: python
  
      >>> import lasio.examples
      >>> las = lasio.examples.open('6038187_v1.2_short.las')
  
  The header sections are stored in the dictionary ``las.sections``:
  
  .. code-block:: python
  
      >>> type(las.sections)
      dict
      >>> las.sections.keys()
      dict_keys(['Version', 'Well', 'Curves', 'Parameter', 'Other'])
  
  These are special names reserved for LAS 1.2 and 2.0 files, as defined by the
  standard. Non-standard header sections are also allowed but not fully parsed.
  
  ==============================  =======================================  ===================================================================
  LAS file                        Read in as                               References in ``LASFile``
  ==============================  =======================================  ===================================================================
  ``~v`` or ``~V``                :class:`lasio.SectionItems`              ``LASFile.version`` and ``LASFile.sections['Version']``
  ``~w`` or ``~W``                :class:`lasio.SectionItems`              ``LASFile.well`` and ``LASFile.sections['Well']``
  ``~c`` or ``~C``                :class:`lasio.SectionItems`              ``LASFile.curves`` and ``LASFile.sections['Curves']``
  ``~p`` or ``~P``                :class:`lasio.SectionItems`              ``LASFile.params`` and ``LASFile.sections['Parameter']``
  ``~o`` or ``~O``                ``str``                                  ``LASFile.other`` and ``LASFile.sections['Other']``
  ``~extra section``              ``str``                                  ``LASFile.sections['extra section']``
  ``~a`` or ``~A``                :class:`numpy.ndarray`                   ``LASFile.data`` or each column is in ``LASFile.curves[...].data``
  ==============================  =======================================  ===================================================================
  
  For example:
  
  .. code-block:: python
  
      >>> las.sections['Version']
      [HeaderItem(mnemonic="VERS", unit="", value="2.0", descr="CWLS LOG ASCII STANDA"),
       HeaderItem(mnemonic="WRAP", unit="", value="NO", descr="ONE LINE PER DEPTH STE")]
  
      >>> las.version
      [HeaderItem(mnemonic="VERS", unit="", value="2.0", descr="CWLS LOG ASCII STANDA"),
       HeaderItem(mnemonic="WRAP", unit="", value="NO", descr="ONE LINE PER DEPTH STE")]
  
  Sections themselves are represented by :class:`lasio.SectionItems`
  objects. This is a ``list`` which has been extended to allow you to access the
  items within by their mnemonic:
  
  .. code-block:: python
  
      >>> las.version.VERS
      HeaderItem(mnemonic="VERS", unit="", value="2.0", descr="CWLS LOG ASCII STANDA")
      >>> las.version['VERS']
      HeaderItem(mnemonic="VERS", unit="", value="2.0", descr="CWLS LOG ASCII STANDA")
      >>> las.version[0]
      HeaderItem(mnemonic="VERS", unit="", value="2.0", descr="CWLS LOG ASCII STANDA")
  
  As you can see, either attribute-style or item-style access is fine.
  
  Let's take a look at the next special section, ``~W``:
  
  .. code-block:: python
  
      >>> las.well
       [HeaderItem(mnemonic="STRT", unit="M", value="0.05", descr="FIRST INDEX VALUE"),
        HeaderItem(mnemonic="STOP", unit="M", value="136.6", descr="LAST INDEX VALUE"),
        HeaderItem(mnemonic="STEP", unit="M", value="0.05", descr="STEP"),
        HeaderItem(mnemonic="NULL", unit="", value="-99999", descr="NULL VALUE"),
        HeaderItem(mnemonic="COMP", unit="", value="", descr="COMP"),
        HeaderItem(mnemonic="WELL", unit="", value="Scorpio E1", descr="WELL"),
        HeaderItem(mnemonic="FLD", unit="", value="", descr=""),
        HeaderItem(mnemonic="LOC", unit="", value="Mt Eba", descr="LOC"),
        HeaderItem(mnemonic="SRVC", unit="", value="", descr=""),
        HeaderItem(mnemonic="CTRY", unit="", value="", descr=""),
        HeaderItem(mnemonic="STAT", unit="", value="SA", descr="STAT"),
        HeaderItem(mnemonic="CNTY", unit="", value="", descr=""),
        HeaderItem(mnemonic="DATE", unit="", value="15/03/2015", descr="DATE"),
        HeaderItem(mnemonic="UWI", unit="", value="6038-187", descr="WUNT")]
  
  The CTRY item is blank. We will set it:
  
  .. code-block:: python
  
      >>> las.well.CTRY = 'Australia'
      >>> las.well.CTRY
      HeaderItem(mnemonic="CTRY", unit="", value="Australia", descr="")
  
  Notice that :class:`lasio.SectionItems` plays a little trick here. It actually
  sets the ``header_item.value`` attribute, instead of replacing the entire
  :class:`lasio.HeaderItem` object.
  
  You can set any of the attributes directly. Let's take an
  
  [... the rest is omitted ...]
files: docs/source/header-section.rst
files: Header section metadata
  =======================
  
  Tutorial
  --------
  
  One of the primary motivations in writing lasio was to be able to reliably
  parse LAS header sections. This is working well for LAS 1.2 and 2.0
  files, and partially for LAS 3.0 files. 
  
  .. note::
  
     lasio does not require LAS files to be strictly compliant with the standards,
     and you should not expect lasio to raise an exception or error for files which
     are clearly not conforming to the standards. The goal of lasio is to parse
     metadata and data quietly, not to fail unnecessarily.
  
  .. code-block:: python
  
      >>> import lasio.examples
      >>> las = lasio.examples.open('6038187_v1.2_short.las')
  
  The header sections are stored in the dictionary ``las.sections``:
  
  .. code-block:: python
  
      >>> type(las.sections)
      dict
      >>> las.sections.keys()
      dict_keys(['Version', 'Well', 'Curves', 'Parameter', 'Other'])
  
  These are special names reserved for LAS 1.2 and 2.0 files, as defined by the
  standard. Non-standard header sections are also allowed but not fully parsed.
  
  ==============================  =======================================  ===================================================================
  LAS file                        Read in as                               References in ``LASFile``
  ==============================  =======================================  ===================================================================
  ``~v`` or ``~V``                :class:`lasio.SectionItems`              ``LASFile.version`` and ``LASFile.sections['Version']``
  ``~w`` or ``~W``                :class:`lasio.SectionItems`              ``LASFile.well`` and ``LASFile.sections['Well']``
  ``~c`` or ``~C``                :class:`lasio.SectionItems`              ``LASFile.curves`` and ``LASFile.sections['Curves']``
  ``~p`` or ``~P``                :class:`lasio.SectionItems`              ``LASFile.params`` and ``LASFile.sections['Parameter']``
  ``~o`` or ``~O``                ``str``                                  ``LASFile.other`` and ``LASFile.sections['Other']``
  ``~extra section``              ``str``                                  ``LASFile.sections['extra section']``
  ``~a`` or ``~A``                :class:`numpy.ndarray`                   ``LASFile.data`` or each column is in ``LASFile.curves[...].data``
  ==============================  =======================================  ===================================================================
  
  For example:
  
  .. code-block:: python
  
      >>> las.sections['Version']
      [HeaderItem(mnemonic="VERS", unit="", value="2.0", descr="CWLS LOG ASCII STANDA"),
       HeaderItem(mnemonic="WRAP", unit="", value="NO", descr="ONE LINE PER DEPTH STE")]
  
      >>> las.version
      [HeaderItem(mnemonic="VERS", unit="", value="2.0", descr="CWLS LOG ASCII STANDA"),
       HeaderItem(mnemonic="WRAP", unit="", value="NO", descr="ONE LINE PER DEPTH STE")]
  
  Sections themselves are represented by :class:`lasio.SectionItems`
  objects. This is a ``list`` which has been extended to allow you to access the
  items within by their mnemonic:
  
  .. code-block:: python
  
      >>> las.version.VERS
      HeaderItem(mnemonic="VERS", unit="", value="2.0", descr="CWLS LOG ASCII STANDA")
      >>> las.version['VERS']
      HeaderItem(mnemonic="VERS", unit="", value="2.0", descr="CWLS LOG ASCII STANDA")
      >>> las.version[0]
      HeaderItem(mnemonic="VERS", unit="", value="2.0", descr="CWLS LOG ASCII STANDA")
  
  As you can see, either attribute-style or item-style access is fine.
  
  Let's take a look at the next special section, ``~W``:
  
  .. code-block:: python
  
      >>> las.well
       [HeaderItem(mnemonic="STRT", unit="M", value="0.05", descr="FIRST INDEX VALUE"),
        HeaderItem(mnemonic="STOP", unit="M", value="136.6", descr="LAST INDEX VALUE"),
        HeaderItem(mnemonic="STEP", unit="M", value="0.05", descr="STEP"),
        HeaderItem(mnemonic="NULL", unit="", value="-99999", descr="NULL VALUE"),
        HeaderItem(mnemonic="COMP", unit="", value="", descr="COMP"),
        HeaderItem(mnemonic="WELL", unit="", value="Scorpio E1", descr="WELL"),
        HeaderItem(mnemonic="FLD", unit="", value="", descr=""),
        HeaderItem(mnemonic="LOC", unit="", value="Mt Eba", descr="LOC"),
        HeaderItem(mnemonic="SRVC", unit="", value="", descr=""),
        HeaderItem(mnemonic="CTRY", unit="", value="", descr=""),
        HeaderItem(mnemonic="STAT", unit="", value="SA", descr="STAT"),
        HeaderItem(mnemonic="CNTY", unit="", value="", descr=""),
        HeaderItem(mnemonic="DATE", unit="", value="15/03/2015", descr="DATE"),
        HeaderItem(mnemonic="UWI", unit="", value="6038-187", descr="WUNT")]
  
  The CTRY item is blank. We will set it:
  
  .. code-block:: python
  
      >>> las.well.CTRY = 'Australia'
      >>> las.well.CTRY
      HeaderItem(mnemonic="CTRY", unit="", value="Australia", descr="")
  
  Notice that :class:`lasio.SectionItems` plays a little trick here. It actually
  sets the ``header_item.value`` attribute, instead of replacing the entire
  :class:`lasio.HeaderItem` object.
  
  You can set any of the attributes directly. Let's take an
  
  [... the rest is omitted ...]
files: docs/source/header-section.rst
files: from __future__ import print_function
  
  try:  # will work in Python 3
      from collections.abc import Sequence
  except ImportError:  # Support Python 2.7
      from collections import Sequence
  
  import csv
  import json
  import logging
  import re
  import sys
  import traceback
  
  # get basestring in py3
  
  try:
      unicode = unicode
  except NameError:
      # 'unicode' is undefined, must be Python 3
      unicode = str
      basestring = (str, bytes)
  else:
      # 'unicode' exists, must be Python 2
      bytes = str
      basestring = basestring
  
  # Required third-party packages available on PyPi:
  
  import numpy as np
  
  # internal lasio imports
  
  from . import exceptions
  from .las_items import HeaderItem, CurveItem, SectionItems, OrderedDict
  from . import defaults
  from . import reader
  from . import writer
  
  logger = logging.getLogger(__name__)
  
  
  class LASFile(object):
  
      """LAS file object.
  
      Keyword Arguments:
          file_ref (file-like object, str): either a filename, an open file
              object, or a string containing the contents of a file.
  
      See these routines for additional keyword arguments you can use when
      reading in a LAS file:
  
      * :func:`lasio.reader.open_with_codecs` - manage issues relate to character
        encodings
      * :meth:`lasio.LASFile.read` - control how NULL values and errors are
        handled during parsing
  
      Attributes:
          encoding (str or None): the character encoding used when reading the
              file in from disk
  
      """
  
      def __init__(self, file_ref=None, **read_kwargs):
          super(LASFile, self).__init__()
          self._text = ""
          self.index_unit = None
          self.index_initial = None
          default_items = defaults.get_default_items()
          self.sections = {
              "Version": default_items["Version"],
              "Well": default_items["Well"],
              "Curves": default_items["Curves"],
              "Parameter": default_items["Parameter"],
              "Other": str(default_items["Other"]),
          }
  
          if not (file_ref is None):
              self.read(file_ref, **read_kwargs)
  
      def read(
          self,
          file_ref,
          ignore_header_errors=False,
          ignore_comments=("#",),
          ignore_data_comments="#",
          mnemonic_case="upper",
          ignore_data=False,
          engine="numpy",
          use_normal_engine_for_wrapped=True,
          read_policy="default",
          null_policy="strict",
          index_unit=None,
          dtypes="auto",
          **kwargs
      ):
          """Read a LAS file.
  
          Arguments:
              file_ref (file-like object, str): either a filename, an open file
                  object, or a string containing the contents of a file.
  
          Keyword Arguments:
              ignore_header_errors (bool): ignore LASHeaderErrors (False by
                  default)
              ignore_comments (sequence/str): ignore lines beginning with these
                  characters e.g. ``("#", '"')`` in header sections.
              ignore_data_comments (str): ignore lines beginning with this
                  character in data sections only.
              mnemonic_case (str): 'preserve': keep the case of HeaderItem mnemonics
                                   'upper': convert all HeaderItem mnemonics to uppercase
                                   'lower': convert all HeaderItem mnemonics to lowercase
              ignore_data (bool): if True, do not read in any of the actual data,
                  just the header metadata. False by default.
              engine (str): "normal": parse data section with normal Python reader
                  (quite slow); "numpy": parse data section with `numpy.genfromtxt` (fast).
                  By default the engine is "numpy".
              use_normal_engine_for_wrapped (bool): if header metadata indicates that
                  the file is wrapped, always use the 'normal' engine. Default is True.
                  The only reason you should use False is if speed is a very high priority
                  and you had files with metadata that incorrectly indicates they are
                  wrapped.
              read_policy (): TODO
              null_policy (str or list): see
                  http://lasio.readthedocs.io/en/latest/data-section.html#handling-invalid-data-indicators-automatically
              index_unit (str): Optionally force-set the index curve's unit to "m" or "ft"
              dtypes ("auto", dict or list): specify the data types for each curve in the
                  ~ASCII data section. If "auto", each curve will be converted to floats if
                  possible and remain as str if not. If a dict you can specify only the
                  curve mnemonics you want to convert as a key. If a list, please specify
                  data types for each curve in order. Note that the conversion currently
                  only occurs via numpy.ndarray.astype() and therefore only a few simple
                  casts will work e.g. `int`, `float`, `str`.
              encoding (str): character encoding to open file_ref with, using
                  :func:`io.open` (this is handled by
                  :func:`lasio.reader.open_with_codecs`)
              encoding_errors (str): 'strict', 'replace' (default), 'ignore' - how to
                  handle errors with encodings (see
                  `this section
                  <https://docs.python.org/3/library/codecs.html#codec-base-classes>`__
                  of the standard library's :mod:`codecs` module for more information)
                  (this is handled by :func:`lasio.reader.open_with_codecs`)
              autodetect_encoding (str or bool): default True to use
                  `chardet <https://github.com/chardet/chardet>`__/`cchardet
                  <https://github.com/PyYoshi/cChardet>`__ to detect encoding.
                  Note if set to False several common encodings will be tried but
                  chardet won't be used.
                  (this is handled by :func:`lasio.reader.open_with_code
  
  [... the rest is omitted ...]
files: lasio/las.py
files: from __future__ import print_function
  
  try:  # will work in Python 3
      from collections.abc import Sequence
  except ImportError:  # Support Python 2.7
      from collections import Sequence
  
  import csv
  import json
  import logging
  import re
  import sys
  import traceback
  
  # get basestring in py3
  
  try:
      unicode = unicode
  except NameError:
      # 'unicode' is undefined, must be Python 3
      unicode = str
      basestring = (str, bytes)
  else:
      # 'unicode' exists, must be Python 2
      bytes = str
      basestring = basestring
  
  # Required third-party packages available on PyPi:
  
  import numpy as np
  
  # internal lasio imports
  
  from . import exceptions
  from .las_items import HeaderItem, CurveItem, SectionItems, OrderedDict
  from . import defaults
  from . import reader
  from . import writer
  
  logger = logging.getLogger(__name__)
  
  
  class LASFile(object):
  
      """LAS file object.
  
      Keyword Arguments:
          file_ref (file-like object, str): either a filename, an open file
              object, or a string containing the contents of a file.
  
      See these routines for additional keyword arguments you can use when
      reading in a LAS file:
  
      * :func:`lasio.reader.open_with_codecs` - manage issues relate to character
        encodings
      * :meth:`lasio.LASFile.read` - control how NULL values and errors are
        handled during parsing
  
      Attributes:
          encoding (str or None): the character encoding used when reading the
              file in from disk
  
      """
  
      def __init__(self, file_ref=None, **read_kwargs):
          super(LASFile, self).__init__()
          self._text = ""
          self.index_unit = None
          self.index_initial = None
          default_items = defaults.get_default_items()
          self.sections = {
              "Version": default_items["Version"],
              "Well": default_items["Well"],
              "Curves": default_items["Curves"],
              "Parameter": default_items["Parameter"],
              "Other": str(default_items["Other"]),
          }
  
          if not (file_ref is None):
              self.read(file_ref, **read_kwargs)
  
      def read(
          self,
          file_ref,
          ignore_header_errors=False,
          ignore_comments=("#",),
          ignore_data_comments="#",
          mnemonic_case="upper",
          ignore_data=False,
          engine="numpy",
          use_normal_engine_for_wrapped=True,
          read_policy="default",
          null_policy="strict",
          index_unit=None,
          dtypes="auto",
          **kwargs
      ):
          """Read a LAS file.
  
          Arguments:
              file_ref (file-like object, str): either a filename, an open file
                  object, or a string containing the contents of a file.
  
          Keyword Arguments:
              ignore_header_errors (bool): ignore LASHeaderErrors (False by
                  default)
              ignore_comments (sequence/str): ignore lines beginning with these
                  characters e.g. ``("#", '"')`` in header sections.
              ignore_data_comments (str): ignore lines beginning with this
                  character in data sections only.
              mnemonic_case (str): 'preserve': keep the case of HeaderItem mnemonics
                                   'upper': convert all HeaderItem mnemonics to uppercase
                                   'lower': convert all HeaderItem mnemonics to lowercase
              ignore_data (bool): if True, do not read in any of the actual data,
                  just the header metadata. False by default.
              engine (str): "normal": parse data section with normal Python reader
                  (quite slow); "numpy": parse data section with `numpy.genfromtxt` (fast).
                  By default the engine is "numpy".
              use_normal_engine_for_wrapped (bool): if header metadata indicates that
                  the file is wrapped, always use the 'normal' engine. Default is True.
                  The only reason you should use False is if speed is a very high priority
                  and you had files with metadata that incorrectly indicates they are
                  wrapped.
              read_policy (): TODO
              null_policy (str or list): see
                  http://lasio.readthedocs.io/en/latest/data-section.html#handling-invalid-data-indicators-automatically
              index_unit (str): Optionally force-set the index curve's unit to "m" or "ft"
              dtypes ("auto", dict or list): specify the data types for each curve in the
                  ~ASCII data section. If "auto", each curve will be converted to floats if
                  possible and remain as str if not. If a dict you can specify only the
                  curve mnemonics you want to convert as a key. If a list, please specify
                  data types for each curve in order. Note that the conversion currently
                  only occurs via numpy.ndarray.astype() and therefore only a few simple
                  casts will work e.g. `int`, `float`, `str`.
              encoding (str): character encoding to open file_ref with, using
                  :func:`io.open` (this is handled by
                  :func:`lasio.reader.open_with_codecs`)
              encoding_errors (str): 'strict', 'replace' (default), 'ignore' - how to
                  handle errors with encodings (see
                  `this section
                  <https://docs.python.org/3/library/codecs.html#codec-base-classes>`__
                  of the standard library's :mod:`codecs` module for more information)
                  (this is handled by :func:`lasio.reader.open_with_codecs`)
              autodetect_encoding (str or bool): default True to use
                  `chardet <https://github.com/chardet/chardet>`__/`cchardet
                  <https://github.com/PyYoshi/cChardet>`__ to detect encoding.
                  Note if set to False several common encodings will be tried but
                  chardet won't be used.
                  (this is handled by :func:`lasio.reader.open_with_code
  
  [... the rest is omitted ...]
files: lasio/las.py
files: import codecs
  import io
  import logging
  import os
  import re
  import sys
  import traceback
  import urllib.request
  
  import numpy as np
  
  from . import defaults
  
  # Convoluted import for StringIO in order to support:
  #
  # - Python 3 - io.StringIO
  # - Python 2 (optimized) - cStringIO.StringIO
  # - Python 2 (all) - StringIO.StringIO
  
  try:
      import cStringIO as StringIO
  except ImportError:
      try:  # cStringIO not available on this system
          import StringIO
      except ImportError:  # Python 3
          from io import StringIO
      else:
          from StringIO import StringIO
  else:
      from StringIO import StringIO
  
  from . import exceptions
  from .las_items import HeaderItem, CurveItem, SectionItems, OrderedDict
  
  
  logger = logging.getLogger(__name__)
  
  URL_REGEXP = re.compile(
      r"^(?:http|ftp)s?://"  # http:// or https://
      r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}"
      r"\.?|[A-Z0-9-]{2,}\.?)|"  # (cont.) domain...
      r"localhost|"  # localhost...
      r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})"  # ...or ip
      r"(?::\d+)?"  # optional port
      r"(?:/?|[/?]\S+)$",
      re.IGNORECASE,
  )
  
  # sow (Split On Whitespace) regex
  sow_regex = re.compile(r"""([^\s"']+)|"([^"]*)"|'([^']*)'""")
  
  
  def define_line_splitter(provisional_delimiter):
      """Define multiple line splitters
  
      return the one that is right for the data delmiter
  
      """
      sow_regex = re.compile(r"""([^\s"']+)|"([^"]*)"|'([^']*)'""")
  
      def split_on_whitespace(line):
          return sow_regex.findall(line)
  
      def split_on_comma(line):
          return line.split(",")
  
      splitters = {
          "SPACE": split_on_whitespace,
          "COMMA": split_on_comma,
      }
  
      return splitters[provisional_delimiter]
  
  
  def check_for_path_obj(file_ref):
      """Check if file_ref is a pathlib.Path object.
  
      If file_ref is a pathlib.Path object, then return its absolute file
      path as a string so it will get processed as other string filenames.
  
      If pathlib is not available, do nothing and return file_ref.
  
      """
      try:
          from pathlib import Path
      except ImportError:
          return file_ref
  
      if isinstance(file_ref, Path):
          return file_ref.absolute().__str__()
      else:
          return file_ref
  
  
  def open_file(file_ref, **encoding_kwargs):
      """Open a file if necessary.
  
      If ``autodetect_encoding=True`` then either ``cchardet`` or ``chardet``
      needs to be installed, or else an ``ImportError`` will be raised.
  
      Arguments:
          file_ref (file-like object, str): either a filename, an open file
              object, or a string containing the contents of a file.
  
      See :func:`lasio.reader.open_with_codecs` for keyword arguments that can be
      used here.
  
      Returns:
          tuple of an open file-like object, and the encoding that
          was used to decode it (if it were read from disk).
  
      """
  
      file_ref = check_for_path_obj(file_ref)
  
      encoding = None
      if isinstance(file_ref, str):  # file_ref != file-like object, so what is it?
          lines = file_ref.splitlines()
          first_line = lines[0]
          if URL_REGEXP.match(first_line):  # it's a URL
              logger.info("Loading URL {}".format(first_line))
  
              response = urllib.request.urlopen(file_ref)
              if response.headers.get_content_charset() is None:
                  if "encoding" in encoding_kwargs:
                      encoding = encoding_kwargs["encoding"]
                  else:
                      encoding = "utf-8"
              else:
                  encoding = response.headers.get_content_charset()
              # newline=None causes StringIO to use universal-newline:
              # Lines in the input can end in '\n', '\r', or '\r\n', and these are
              # translated into '\n' before being returned to the caller.
              file_ref = StringIO(response.read().decode(encoding), newline=None)
              logger.debug("Retrieved data decoded via {}".format(encoding))
          elif len(lines) > 1:  # it's LAS data as a string.
              file_ref = StringIO(file_ref)
          else:  # it must be a filename
              file_ref, encoding = open_with_codecs(first_line, **encoding_kwargs)
      return file_ref, encoding
  
  
  def open_with_codecs(
      filename,
      encoding=None,
      encoding_errors="replace",
      autodetect_encoding=True,
      autodetect_encoding_chars=4000,
  ):
      """
      Read Unicode data from file.
  
      Arguments:
          filename (str): path to file
  
      Keyword Arguments:
          encoding (str): character encoding to open file_ref with, using
              :func:`io.open`.
          encoding_errors (str): 'strict', 'replace' (default), 'ignore' - how to
              handle errors with encodings (see
              `this section
              <https://docs.python.org/3/library/codecs.html#codec-base-classes>`__
              of the standard library's :mod:`codecs` module for more information)
          autodetect_encoding (str or bool): default True to use
              `chardet <https://github.com/chardet/chardet>`__/`cchardet
  
  [... the rest is omitted ...]
files: lasio/reader.py
files: import codecs
  import io
  import logging
  import os
  import re
  import sys
  import traceback
  import urllib.request
  
  import numpy as np
  
  from . import defaults
  
  # Convoluted import for StringIO in order to support:
  #
  # - Python 3 - io.StringIO
  # - Python 2 (optimized) - cStringIO.StringIO
  # - Python 2 (all) - StringIO.StringIO
  
  try:
      import cStringIO as StringIO
  except ImportError:
      try:  # cStringIO not available on this system
          import StringIO
      except ImportError:  # Python 3
          from io import StringIO
      else:
          from StringIO import StringIO
  else:
      from StringIO import StringIO
  
  from . import exceptions
  from .las_items import HeaderItem, CurveItem, SectionItems, OrderedDict
  
  
  logger = logging.getLogger(__name__)
  
  URL_REGEXP = re.compile(
      r"^(?:http|ftp)s?://"  # http:// or https://
      r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+(?:[A-Z]{2,6}"
      r"\.?|[A-Z0-9-]{2,}\.?)|"  # (cont.) domain...
      r"localhost|"  # localhost...
      r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})"  # ...or ip
      r"(?::\d+)?"  # optional port
      r"(?:/?|[/?]\S+)$",
      re.IGNORECASE,
  )
  
  # sow (Split On Whitespace) regex
  sow_regex = re.compile(r"""([^\s"']+)|"([^"]*)"|'([^']*)'""")
  
  
  def define_line_splitter(provisional_delimiter):
      """Define multiple line splitters
  
      return the one that is right for the data delmiter
  
      """
      sow_regex = re.compile(r"""([^\s"']+)|"([^"]*)"|'([^']*)'""")
  
      def split_on_whitespace(line):
          return sow_regex.findall(line)
  
      def split_on_comma(line):
          return line.split(",")
  
      splitters = {
          "SPACE": split_on_whitespace,
          "COMMA": split_on_comma,
      }
  
      return splitters[provisional_delimiter]
  
  
  def check_for_path_obj(file_ref):
      """Check if file_ref is a pathlib.Path object.
  
      If file_ref is a pathlib.Path object, then return its absolute file
      path as a string so it will get processed as other string filenames.
  
      If pathlib is not available, do nothing and return file_ref.
  
      """
      try:
          from pathlib import Path
      except ImportError:
          return file_ref
  
      if isinstance(file_ref, Path):
          return file_ref.absolute().__str__()
      else:
          return file_ref
  
  
  def open_file(file_ref, **encoding_kwargs):
      """Open a file if necessary.
  
      If ``autodetect_encoding=True`` then either ``cchardet`` or ``chardet``
      needs to be installed, or else an ``ImportError`` will be raised.
  
      Arguments:
          file_ref (file-like object, str): either a filename, an open file
              object, or a string containing the contents of a file.
  
      See :func:`lasio.reader.open_with_codecs` for keyword arguments that can be
      used here.
  
      Returns:
          tuple of an open file-like object, and the encoding that
          was used to decode it (if it were read from disk).
  
      """
  
      file_ref = check_for_path_obj(file_ref)
  
      encoding = None
      if isinstance(file_ref, str):  # file_ref != file-like object, so what is it?
          lines = file_ref.splitlines()
          first_line = lines[0]
          if URL_REGEXP.match(first_line):  # it's a URL
              logger.info("Loading URL {}".format(first_line))
  
              response = urllib.request.urlopen(file_ref)
              if response.headers.get_content_charset() is None:
                  if "encoding" in encoding_kwargs:
                      encoding = encoding_kwargs["encoding"]
                  else:
                      encoding = "utf-8"
              else:
                  encoding = response.headers.get_content_charset()
              # newline=None causes StringIO to use universal-newline:
              # Lines in the input can end in '\n', '\r', or '\r\n', and these are
              # translated into '\n' before being returned to the caller.
              file_ref = StringIO(response.read().decode(encoding), newline=None)
              logger.debug("Retrieved data decoded via {}".format(encoding))
          elif len(lines) > 1:  # it's LAS data as a string.
              file_ref = StringIO(file_ref)
          else:  # it must be a filename
              file_ref, encoding = open_with_codecs(first_line, **encoding_kwargs)
      return file_ref, encoding
  
  
  def open_with_codecs(
      filename,
      encoding=None,
      encoding_errors="replace",
      autodetect_encoding=True,
      autodetect_encoding_chars=4000,
  ):
      """
      Read Unicode data from file.
  
      Arguments:
          filename (str): path to file
  
      Keyword Arguments:
          encoding (str): character encoding to open file_ref with, using
              :func:`io.open`.
          encoding_errors (str): 'strict', 'replace' (default), 'ignore' - how to
              handle errors with encodings (see
              `this section
              <https://docs.python.org/3/library/codecs.html#codec-base-classes>`__
              of the standard library's :mod:`codecs` module for more information)
          autodetect_encoding (str or bool): default True to use
              `chardet <https://github.com/chardet/chardet>`__/`cchardet
  
  [... the rest is omitted ...]
files: lasio/reader.py