Update the project storage class
This commit is contained in:
@@ -40,7 +40,7 @@ from novelwriter.common import (
|
||||
)
|
||||
from novelwriter.constants import nwFiles
|
||||
|
||||
if TYPE_CHECKING:
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from novelwriter.core.status import NWStatus
|
||||
from novelwriter.core.projectdata import NWProjectData
|
||||
|
||||
|
||||
+53
-64
@@ -1,7 +1,6 @@
|
||||
"""
|
||||
novelWriter – Project Storage Class
|
||||
===================================
|
||||
The main class handling the project storage
|
||||
|
||||
File History:
|
||||
Created: 2022-11-01 [2.0rc2] NWStorage
|
||||
@@ -22,10 +21,12 @@ General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from time import time
|
||||
from typing import TYPE_CHECKING
|
||||
from pathlib import Path
|
||||
from zipfile import ZIP_DEFLATED, ZIP_STORED, ZipFile
|
||||
|
||||
@@ -36,29 +37,32 @@ from novelwriter.constants import nwFiles
|
||||
from novelwriter.core.document import NWDocument
|
||||
from novelwriter.core.projectxml import ProjectXMLReader, ProjectXMLWriter
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from novelwriter.core.project import NWProject
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NWStorage:
|
||||
"""Core: Project Storage Class
|
||||
|
||||
The class that handles all paths related to the project storage.
|
||||
"""
|
||||
|
||||
MODE_INACTIVE = 0
|
||||
MODE_INPLACE = 1
|
||||
MODE_ARCHIVE = 2
|
||||
|
||||
def __init__(self, theProject):
|
||||
|
||||
self.theProject = theProject
|
||||
|
||||
def __init__(self, project: NWProject):
|
||||
self._project = project
|
||||
self._storagePath = None
|
||||
self._runtimePath = None
|
||||
self._lockFilePath = None
|
||||
self._openMode = self.MODE_INACTIVE
|
||||
|
||||
return
|
||||
|
||||
def clear(self):
|
||||
"""Reset internal variables.
|
||||
"""
|
||||
"""Reset internal variables."""
|
||||
self._storagePath = None
|
||||
self._runtimePath = None
|
||||
self._openMode = self.MODE_INACTIVE
|
||||
@@ -69,15 +73,17 @@ class NWStorage:
|
||||
##
|
||||
|
||||
@property
|
||||
def storagePath(self):
|
||||
def storagePath(self) -> Path | None:
|
||||
"""Get the path where the project is saved."""
|
||||
return self._storagePath
|
||||
|
||||
@property
|
||||
def runtimePath(self):
|
||||
def runtimePath(self) -> Path | None:
|
||||
"""Get the path where the project is saved at runtime."""
|
||||
return self._runtimePath
|
||||
|
||||
@property
|
||||
def contentPath(self):
|
||||
def contentPath(self) -> Path | None:
|
||||
"""Return the path used for project content. The folder must
|
||||
already exist, otherwise this property is None.
|
||||
"""
|
||||
@@ -94,12 +100,11 @@ class NWStorage:
|
||||
# Core Methods
|
||||
##
|
||||
|
||||
def isOpen(self):
|
||||
"""Check if the storage location is open.
|
||||
"""
|
||||
def isOpen(self) -> bool:
|
||||
"""Check if the storage location is open."""
|
||||
return self._runtimePath is not None
|
||||
|
||||
def openProjectInPlace(self, path, newProject=False):
|
||||
def openProjectInPlace(self, path: str | Path, newProject: bool = False) -> bool:
|
||||
"""Open a novelWriter project in-place. That is, it is opened
|
||||
directly from a project folder.
|
||||
"""
|
||||
@@ -124,13 +129,15 @@ class NWStorage:
|
||||
|
||||
return True
|
||||
|
||||
def openProjectArchive(self, path): # pragma: no cover
|
||||
"""Placeholder for later implementation. See #977.
|
||||
def openProjectArchive(self, path: str | Path) -> bool: # pragma: no cover
|
||||
"""Open the project from a single file.
|
||||
Placeholder for later implementation. See #977.
|
||||
"""
|
||||
return False
|
||||
|
||||
def runPostSaveTasks(self, autoSave=False): # pragma: no cover
|
||||
def runPostSaveTasks(self, autoSave: bool = False) -> bool: # pragma: no cover
|
||||
"""Run tasks after the project has been saved.
|
||||
Placeholder for later implementation. See #977.
|
||||
"""
|
||||
if self._openMode == self.MODE_INPLACE:
|
||||
# Nothing to do, so we just return
|
||||
@@ -139,8 +146,7 @@ class NWStorage:
|
||||
return True
|
||||
|
||||
def closeSession(self):
|
||||
"""Run tasks related to closing the session.
|
||||
"""
|
||||
"""Run tasks related to closing the session."""
|
||||
# Clear lockfile
|
||||
self.clear()
|
||||
return
|
||||
@@ -149,51 +155,35 @@ class NWStorage:
|
||||
# Content Access Methods
|
||||
##
|
||||
|
||||
def getXmlReader(self):
|
||||
"""Return a properly configured ProjectXMLReader instance.
|
||||
"""
|
||||
def getXmlReader(self) -> ProjectXMLReader | None:
|
||||
"""Return a properly configured ProjectXMLReader instance."""
|
||||
if self._runtimePath is None:
|
||||
return None
|
||||
|
||||
projFile = self._runtimePath / nwFiles.PROJ_FILE
|
||||
xmlReader = ProjectXMLReader(projFile)
|
||||
|
||||
return xmlReader
|
||||
|
||||
def getXmlWriter(self):
|
||||
"""Return a properly configured ProjectXMLWriter instance.
|
||||
"""
|
||||
def getXmlWriter(self) -> ProjectXMLWriter | None:
|
||||
"""Return a properly configured ProjectXMLWriter instance."""
|
||||
if self._runtimePath is None:
|
||||
return None
|
||||
|
||||
xmlWriter = ProjectXMLWriter(self._runtimePath)
|
||||
|
||||
return xmlWriter
|
||||
|
||||
def getDocument(self, tHandle):
|
||||
"""Return a document wrapper object.
|
||||
"""
|
||||
def getDocument(self, tHandle: str) -> NWDocument:
|
||||
"""Return a document wrapper object."""
|
||||
if self._runtimePath is not None:
|
||||
return NWDocument(self.theProject, tHandle)
|
||||
return NWDocument(self.theProject, None)
|
||||
return NWDocument(self._project, tHandle)
|
||||
return NWDocument(self._project, None)
|
||||
|
||||
def getMetaFile(self, fileName):
|
||||
"""Return the path to a file in the project meta folder.
|
||||
"""
|
||||
def getMetaFile(self, fileName: str) -> Path | None:
|
||||
"""Return the path to a file in the project meta folder."""
|
||||
if self._runtimePath is not None:
|
||||
return self._runtimePath / "meta" / fileName
|
||||
return None
|
||||
|
||||
def getCacheFile(self, fileName):
|
||||
"""Return the path to a file in the project cache folder.
|
||||
"""
|
||||
if self._runtimePath is not None:
|
||||
return self._runtimePath / "cache" / fileName
|
||||
return None
|
||||
|
||||
def readLockFile(self):
|
||||
"""Read the project lock file.
|
||||
"""
|
||||
def readLockFile(self) -> list:
|
||||
"""Read the project lock file."""
|
||||
if self._lockFilePath is None:
|
||||
return ["ERROR"]
|
||||
|
||||
@@ -212,9 +202,8 @@ class NWStorage:
|
||||
|
||||
return lines
|
||||
|
||||
def writeLockFile(self):
|
||||
"""Write the project lock file.
|
||||
"""
|
||||
def writeLockFile(self) -> bool:
|
||||
"""Write the project lock file."""
|
||||
if self._lockFilePath is None:
|
||||
return False
|
||||
|
||||
@@ -231,9 +220,8 @@ class NWStorage:
|
||||
|
||||
return True
|
||||
|
||||
def clearLockFile(self):
|
||||
"""Remove the lock file, if it exists.
|
||||
"""
|
||||
def clearLockFile(self) -> bool:
|
||||
"""Remove the lock file, if it exists."""
|
||||
if self._lockFilePath is None:
|
||||
return False
|
||||
|
||||
@@ -247,7 +235,7 @@ class NWStorage:
|
||||
|
||||
return True
|
||||
|
||||
def zipIt(self, target, compression=None):
|
||||
def zipIt(self, target: str | Path, compression: int | None = None) -> bool:
|
||||
"""Zip the content of the project at its runtime location into a
|
||||
zip file. This process will only grab files that are supposed to
|
||||
be in the project. All non-project files will be left out.
|
||||
@@ -291,9 +279,8 @@ class NWStorage:
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _prepareStorage(self, checkLegacy=True, newProject=False):
|
||||
"""Prepare the storage area for the project.
|
||||
"""
|
||||
def _prepareStorage(self, checkLegacy: bool = True, newProject: bool = False) -> bool:
|
||||
"""Prepare the storage area for the project."""
|
||||
path = self._runtimePath
|
||||
if not isinstance(path, Path):
|
||||
logger.error("No path set")
|
||||
@@ -318,7 +305,6 @@ class NWStorage:
|
||||
try:
|
||||
path.mkdir(exist_ok=True)
|
||||
(path / "content").mkdir(exist_ok=True)
|
||||
(path / "cache").mkdir(exist_ok=True)
|
||||
(path / "meta").mkdir(exist_ok=True)
|
||||
except Exception as exc:
|
||||
logger.error("Failed to create required project folders", exc_info=exc)
|
||||
@@ -385,8 +371,7 @@ class NWStorage:
|
||||
return
|
||||
|
||||
def _deleteDeprecatedFiles(self, path: Path):
|
||||
"""Delete files that are no longer used by novelWriter.
|
||||
"""
|
||||
"""Delete files that are no longer used by novelWriter."""
|
||||
remove = [
|
||||
path / "meta" / "mainOptions.json", # Replaced in 0.5
|
||||
path / "meta" / "exportOptions.json", # Replaced in 0.5
|
||||
@@ -394,16 +379,20 @@ class NWStorage:
|
||||
path / "meta" / "timelineOptions.json", # Replaced in 0.5
|
||||
path / "meta" / "docMergeOptions.json", # Replaced in 0.5
|
||||
path / "meta" / "sessionLogOptions.json", # Replaced in 0.5
|
||||
path / "cache" / "prevBuild.json", # Dropped in 2.1 Beta 1
|
||||
path / "cache", # Dropped in 2.1 Beta 1
|
||||
path / "ToC.json", # Dropped in 1.0 RC 1
|
||||
]
|
||||
for item in remove:
|
||||
if item.is_file():
|
||||
if item.exists():
|
||||
try:
|
||||
item.unlink()
|
||||
if item.is_dir():
|
||||
item.rmdir()
|
||||
else:
|
||||
item.unlink()
|
||||
logger.info("Deleted: %s", item)
|
||||
except Exception as exc:
|
||||
logger.warning("Failed to delete: %s", item, exc_info=exc)
|
||||
|
||||
return
|
||||
|
||||
# END Class NWStorage
|
||||
|
||||
Reference in New Issue
Block a user