# Path: plumbing/cache.py
# class property_cached(object):
#     """
#     This decorator converts a method with a single self argument
#     into a property cached on the instance.
# 
#     In other words, it's like `@property` but memoized.
# 
#         from plumbing.cache import property_cached
# 
#         class Square:
#             def __init__(self, size):
#                 self.size = size
# 
#             @property_cached
#             def area(self):
#                 print("Evaluating...")
#                 return self.size * self.size
# 
#         shape = Square(5)
#         print(shape.size)
#         print(shape.area)  # Area computed the first time
#         shape.area = 99    # Now update an attribute
#         print(shape.area)  # Same result returned, not recomputed
#         del shape.area     # This will purge the cache
#         print(shape.area)  # Updated result, it's computed again
#     """
# 
#     def __init__(self, func):
#         self.func    = func
#         self.__doc__ = getattr(func, '__doc__')
#         self.name    = self.func.__name__
# 
#     def __get__(self, instance, owner):
#         """
#         If you see the current source code in a seemingly unrelated part of
#         an auto-generated documentation, it means the program making the
#         documentation was unable to correctly traverse a decorated property.
#         """
#         # For debugging purposes #
#         if False: print("-> property cached `%s`" % instance)
#         # If called from a class #
#         if instance is None: return self
#         # Does a cache exist for this instance? #
#         self.check_cache(instance)
#         # Is the answer in the cache? #
#         if self.name in instance.__cache__: return instance.__cache__[self.name]
#         # If not we will compute it #
#         if inspect.isgeneratorfunction(self.func): result = tuple(self.func(instance))
#         else:                                      result = self.func(instance)
#         # Let's store the answer for later #
#         instance.__cache__[self.name] = result
#         # Return #
#         return result
# 
#     def __set__(self, instance, value):
#         # Does a cache exist for this instance? #
#         self.check_cache(instance)
#         # Overwrite the value #
#         instance.__cache__[self.name] = value
# 
#     def __delete__(self, instance):
#         # Does a cache exist for this instance? #
#         self.check_cache(instance)
#         # Remove the key #
#         instance.__cache__.pop(self.name, None)
# 
#     def check_cache(self, instance):
#         if '__cache__' not in instance.__dict__: instance.__cache__ = {}


from autopaths import Path
from plumbing.cache import property_cached
import pandas
            df.to_excel(self.writer,
                        sheet_name = key,
                        startrow   = row,
                        startcol   = self.indentation)
            # Increment #
            row += len(df.index) + self.spacing

###############################################################################
class ConvertExcelToCSV:
    """
    Will convert an excel file into its CSV equivalent.
    Can support multiple work sheets into a single CSV.
    """

    def __init__(self, source_path, dest_path, **kwargs):
        # Record attributes #
        self.source = Path(source_path)
        self.dest   = Path(dest_path)
        # Keep the kwargs too #
        self.kwargs = kwargs
        # Check directory case #
        if self.dest.endswith('/'):
            self.dest = self.dest + self.source.filename
            self.dest = self.dest.replace_extension('csv')

    def __call__(self):
        """Are we mono or multi sheet?"""
        if len(self.handle.sheet_names) > 1: self.multi_sheet()
        else:                                self.mono_sheet()


# Path: plumbing/cache.py
# class property_cached(object):
#     """
#     This decorator converts a method with a single self argument
#     into a property cached on the instance.
# 
#     In other words, it's like `@property` but memoized.
# 
#         from plumbing.cache import property_cached
# 
#         class Square:
#             def __init__(self, size):
#                 self.size = size
# 
#             @property_cached
#             def area(self):
#                 print("Evaluating...")
#                 return self.size * self.size
# 
#         shape = Square(5)
#         print(shape.size)
#         print(shape.area)  # Area computed the first time
#         shape.area = 99    # Now update an attribute
#         print(shape.area)  # Same result returned, not recomputed
#         del shape.area     # This will purge the cache
#         print(shape.area)  # Updated result, it's computed again
#     """
# 
#     def __init__(self, func):
#         self.func    = func
#         self.__doc__ = getattr(func, '__doc__')
#         self.name    = self.func.__name__
# 
#     def __get__(self, instance, owner):
#         """
#         If you see the current source code in a seemingly unrelated part of
#         an auto-generated documentation, it means the program making the
#         documentation was unable to correctly traverse a decorated property.
#         """
#         # For debugging purposes #
#         if False: print("-> property cached `%s`" % instance)
#         # If called from a class #
#         if instance is None: return self
#         # Does a cache exist for this instance? #
#         self.check_cache(instance)
#         # Is the answer in the cache? #
#         if self.name in instance.__cache__: return instance.__cache__[self.name]
#         # If not we will compute it #
#         if inspect.isgeneratorfunction(self.func): result = tuple(self.func(instance))
#         else:                                      result = self.func(instance)
#         # Let's store the answer for later #
#         instance.__cache__[self.name] = result
#         # Return #
#         return result
# 
#     def __set__(self, instance, value):
#         # Does a cache exist for this instance? #
#         self.check_cache(instance)
#         # Overwrite the value #
#         instance.__cache__[self.name] = value
# 
#     def __delete__(self, instance):
#         # Does a cache exist for this instance? #
#         self.check_cache(instance)
#         # Remove the key #
#         instance.__cache__.pop(self.name, None)
# 
#     def check_cache(self, instance):
#         if '__cache__' not in instance.__dict__: instance.__cache__ = {}


# Path: plumbing/xls_tables.py
from autopaths import Path
from plumbing.cache import property_cached
import pandas

            df.to_excel(self.writer,
                        sheet_name = key,
                        startrow   = row,
                        startcol   = self.indentation)
            # Increment #
            row += len(df.index) + self.spacing

###############################################################################
class ConvertExcelToCSV:
    """
    Will convert an excel file into its CSV equivalent.
    Can support multiple work sheets into a single CSV.
    """

    def __init__(self, source_path, dest_path, **kwargs):
        # Record attributes #
        self.source = Path(source_path)
        self.dest   = Path(dest_path)
        # Keep the kwargs too #
        self.kwargs = kwargs
        # Check directory case #
        if self.dest.endswith('/'):
            self.dest = self.dest + self.source.filename
            self.dest = self.dest.replace_extension('csv')

    def __call__(self):
        """Are we mono or multi sheet?"""
        if len(self.handle.sheet_names) > 1: self.multi_sheet()
        else:                                self.mono_sheet()


    @property_cached