<commit_before>
"""
Functions and classes relating to files, references, and system calls.

In particular, the system module contains the functionality of maya.cmds.file. The file command should not be imported into
the default namespace because it conflicts with python's builtin file class. Since the file command has so many flags,
we decided to kill two birds with one stone: by breaking the file command down into multiple functions -- one for each
primary flag -- the resulting functions are more readable and allow the file command's functionality to be used directly
within the pymel namespace.

for example, instead of this:

    >>> res = cmds.file( 'test.ma', exportAll=1, preserveReferences=1, type='mayaAscii', force=1 ) # doctest: +SKIP

you can do this:

    >>> expFile = exportAll( 'test.ma', preserveReferences=1, force=1)

some of the new commands were changed slightly from their flag name to avoid name clashes and to add to readability:

    >>> importFile( expFile )  # flag was called import, but that's a python keyword
    >>> ref = createReference( expFile )
    >>> ref # doctest: +ELLIPSIS
    FileReference(u'.../test.ma', refnode=u'testRN')

Notice that the 'type' flag is set automatically for you when your path includes a '.mb' or '.ma' extension.

Paths returned by these commands are either a `Path` or a `FileReference`, so you can use object-oriented path methods with
the results::

    >>> expFile.exists()
    True
    >>> expFile.remove() # doctest: +ELLIPSIS
    Path('.../test.ma')

"""

import sys
import os
import warnings

import maya.mel as _mel
import maya.OpenMaya as _OpenMaya

from pymel.util.scanf import fscanf
from pymel.util.decoration import decorator
import pymel.util as _util
import pymel.internal.factories as _factories
import pymel.internal as _internal
import pymel.internal.pmcmds as cmds
import pymel.versions as versions

_logger = _internal.getLogger(__name__)


try:
    from pymel.internal.startup import pymel_options as _pymel_options
    # attempt to import a custom path class to use as the base for pymel's Path class
    basePathName = _pymel_options['path_class']
    buf = basePathName.split('.')
    moduleName = '.'.join(buf[:-1])
    className = buf[-1]
    try:
        pathModule = __import__(moduleName, globals(), locals(), [''])
    except Exception, e:
        _logger.warning("Could not import %r module containing custom base Path class: %s" % (moduleName, str(e)))
        raise AssertionError

    try:
        pathClass = getattr(pathModule, className)
        _logger.info("Using custom path class %s" % (basePathName))
    except AttributeError, e:
        _logger.warning("Custom path class %s could not be found in module %s" % (className, pathModule))
        raise AssertionError
except (KeyError, AssertionError):
    pathClass = _util.path


def _getTypeFromExtension(path, mode='write'):
    '''
    Parameters
    ----------
    path : str
        path from with to pull the extension from - note that it may NOT be
        ONLY the extension - ie, "obj" and ".obj", will not work, but
        "foo.obj" will
    mode : {'write', 'read'}
        the type is basically a string name of a file translator, which can
        have different ones registered for reading or writing; this specifies
        whether you're looking for the read or write translator
    '''
    ext = Path(path).ext
    return str(Translator.fromExtension(ext, mode=mode))

def _setTypeKwargFromExtension(path, kwargs, mode='write'):
    if 'type' not in kwargs and 'typ' not in kwargs:
        try:
            fileType = _getTypeFromExtension(path, mode=mode)
        except Exception:
            pass
        else:
            if fileType and fileType != 'None':
                kwargs['type'] = fileType

# Bring the MGlobal.display* methods into this namespace, for convenience
displayError = _OpenMaya.MGlobal.displayError
displayWarning = _OpenMaya.MGlobal.displayWarning
displayInfo = _OpenMaya.MGlobal.displayInfo

def feof(fileid):
    """Reproduces the behavior of the mel command of the same name. if writing pymel scripts from scratch,
    you should use a more pythonic construct for looping through files:

    >>> f = open('myfile.txt') # doctest: +SKIP
    ... for line in f:
    ...     print line

    This command is provided for python scripts generated by mel2py"""

    pos = fileid.tell()
    fileid.seek(0, 2)  # goto end of file
    end = fileid.tell()  # get final position
    fileid.seek(pos)
    return pos == end


@_factories.addMelDocs('file', 'sceneName')
def sceneName():
    # We don't just use cmds.file(q=1, sceneName=1)
    # because it was sometimes returning an empty string,
    # even when there was a valid file
    name = Path(_OpenMaya.MFileIO.currentFile())
    if name.basename() == untitledFileName() and \
            cmds.file(q=1, sceneName=1) == '':
        return Path()
    return name

def untitledFileName():
    """
    Obtain the base filename used for untitled scenes. In localized environments, this string will contain a translated value.
    """
    return _mel.eval('untitledFileName()')

class UndoChunk(object):

    '''Context manager for encapsulating code in a single undo.

    Use in a with statement
    Wrapper for cmds.undoInfo(openChunk=1)/cmds.undoInfo(closeChunk=1)

    >>> import pymel.core as pm
    >>> pm.ls("MyNode*", type='transform')
    []<commit_msg>return empty path for untitled string instead of /path/to/untitled.mb<commit_after>
"""
Functions and classes relating to files, references, and system calls.

In particular, the system module contains the functionality of maya.cmds.file. The file command should not be imported into
the default namespace because it conflicts with python's builtin file class. Since the file command has so many flags,
we decided to kill two birds with one stone: by breaking the file command down into multiple functions -- one for each
primary flag -- the resulting functions are more readable and allow the file command's functionality to be used directly
within the pymel namespace.

for example, instead of this:

    >>> res = cmds.file( 'test.ma', exportAll=1, preserveReferences=1, type='mayaAscii', force=1 ) # doctest: +SKIP

you can do this:

    >>> expFile = exportAll( 'test.ma', preserveReferences=1, force=1)

some of the new commands were changed slightly from their flag name to avoid name clashes and to add to readability:

    >>> importFile( expFile )  # flag was called import, but that's a python keyword
    >>> ref = createReference( expFile )
    >>> ref # doctest: +ELLIPSIS
    FileReference(u'.../test.ma', refnode=u'testRN')

Notice that the 'type' flag is set automatically for you when your path includes a '.mb' or '.ma' extension.

Paths returned by these commands are either a `Path` or a `FileReference`, so you can use object-oriented path methods with
the results::

    >>> expFile.exists()
    True
    >>> expFile.remove() # doctest: +ELLIPSIS
    Path('.../test.ma')

"""

import sys
import os
import warnings

import maya.mel as _mel
import maya.OpenMaya as _OpenMaya

from pymel.util.scanf import fscanf
from pymel.util.decoration import decorator
import pymel.util as _util
import pymel.internal.factories as _factories
import pymel.internal as _internal
import pymel.internal.pmcmds as cmds
import pymel.versions as versions

_logger = _internal.getLogger(__name__)


try:
    from pymel.internal.startup import pymel_options as _pymel_options
    # attempt to import a custom path class to use as the base for pymel's Path class
    basePathName = _pymel_options['path_class']
    buf = basePathName.split('.')
    moduleName = '.'.join(buf[:-1])
    className = buf[-1]
    try:
        pathModule = __import__(moduleName, globals(), locals(), [''])
    except Exception, e:
        _logger.warning("Could not import %r module containing custom base Path class: %s" % (moduleName, str(e)))
        raise AssertionError

    try:
        pathClass = getattr(pathModule, className)
        _logger.info("Using custom path class %s" % (basePathName))
    except AttributeError, e:
        _logger.warning("Custom path class %s could not be found in module %s" % (className, pathModule))
        raise AssertionError
except (KeyError, AssertionError):
    pathClass = _util.path


def _getTypeFromExtension(path, mode='write'):
    '''
    Parameters
    ----------
    path : str
        path from with to pull the extension from - note that it may NOT be
        ONLY the extension - ie, "obj" and ".obj", will not work, but
        "foo.obj" will
    mode : {'write', 'read'}
        the type is basically a string name of a file translator, which can
        have different ones registered for reading or writing; this specifies
        whether you're looking for the read or write translator
    '''
    ext = Path(path).ext
    return str(Translator.fromExtension(ext, mode=mode))

def _setTypeKwargFromExtension(path, kwargs, mode='write'):
    if 'type' not in kwargs and 'typ' not in kwargs:
        try:
            fileType = _getTypeFromExtension(path, mode=mode)
        except Exception:
            pass
        else:
            if fileType and fileType != 'None':
                kwargs['type'] = fileType

# Bring the MGlobal.display* methods into this namespace, for convenience
displayError = _OpenMaya.MGlobal.displayError
displayWarning = _OpenMaya.MGlobal.displayWarning
displayInfo = _OpenMaya.MGlobal.displayInfo

def feof(fileid):
    """Reproduces the behavior of the mel command of the same name. if writing pymel scripts from scratch,
    you should use a more pythonic construct for looping through files:

    >>> f = open('myfile.txt') # doctest: +SKIP
    ... for line in f:
    ...     print line

    This command is provided for python scripts generated by mel2py"""

    pos = fileid.tell()
    fileid.seek(0, 2)  # goto end of file
    end = fileid.tell()  # get final position
    fileid.seek(pos)
    return pos == end


@_factories.addMelDocs('file', 'sceneName')
def sceneName():
    # We don't just use cmds.file(q=1, sceneName=1)
    # because it was sometimes returning an empty string,
    # even when there was a valid file
    name = Path(_OpenMaya.MFileIO.currentFile())
    if name.basename().startswith(untitledFileName()) and \
            cmds.file(q=1, sceneName=1) == '':
        return Path()
    else:
        return name

def untitledFileName():
    """
    Obtain the base filename used for untitled scenes. In localized environments, this string will contain a translated value.
    """
    return _mel.eval('untitledFileName()')

class UndoChunk(object):

    '''Context manager for encapsulating code in a single undo.

    Use in a with statement
    Wrapper for cmds.undoInfo(openChunk=1)/cmds.undoInfo(closeChunk=1)

    >>> import pymel.core as pm
    >>> pm.ls("MyNode*", type='transform')
    []
{'fraction_non_alphanumeric': 0.09426868408986704, 'fraction_numerical': 0.0029344337459880788, 'mean_word_length': 3.343289526085225, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 63, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '878591', 'n_tokens_mistral': 3153, 'n_tokens_neox': 3082, 'n_words': 1208}