Skip to content

Commit

Permalink
Drop support for .NET Standard 2.1.
Browse files Browse the repository at this point in the history
#108: OK. Let's do it.
  • Loading branch information
CXuesong committed May 12, 2024
1 parent bba8b7a commit 6655faa
Show file tree
Hide file tree
Showing 6 changed files with 15 additions and 168 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# Wiki Client Library

[![Build Status](https://github.com/CXuesong/WikiClientLibrary/workflows/CI/badge.svg?branch=master)](https://github.com/CXuesong/WikiClientLibrary/actions?query=workflow%3ACI) [![Gitter](https://badges.gitter.im/CXuesong/WikiClientLibrary.svg?style=flat-square)](https://gitter.im/CXuesong/WikiClientLibrary?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![CI](https://github.com/CXuesong/WikiClientLibrary/actions/workflows/ci.yml/badge.svg)](https://github.com/CXuesong/WikiClientLibrary/actions/workflows/ci.yml) [![Gitter](https://badges.gitter.im/CXuesong/WikiClientLibrary.svg?style=flat-square)](https://gitter.im/CXuesong/WikiClientLibrary?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)

A hand-crafted asynchronous [MediaWiki](https://www.mediawiki.org/) API client library for wiki sites (including [Wikipedia](https://www.wikipedia.org/) and its sister projects, as well as [FANDOM](<https://community.fandom.com/wiki/Community_Central>) and [Wikia.org](<https://www.wikia.org/>). The library targets at .NET Standard 2.1 & .NET 5.0 (See [Supported Platforms](https://docs.microsoft.com/en-us/dotnet/standard/net-standard#net-platforms-support)), and focuses on the API compatibility with MediaWiki 1.19, as well as the state-of-art APIs of MediaWiki (i.e. 1.34-wmf, as in 2019-05). Other versions in between are hopefully also compatible.
A hand-crafted asynchronous [MediaWiki](https://www.mediawiki.org/) API client library for wiki sites (including [Wikipedia](https://www.wikipedia.org/) and its sister projects, as well as [FANDOM](<https://community.fandom.com/wiki/Community_Central>) and [Wikia.org](<https://www.wikia.org/>). The library targets at .NET 6.0 & 8.0 (See [Supported Platforms](https://docs.microsoft.com/en-us/dotnet/standard/net-standard#net-platforms-support)), and focuses on the API compatibility with MediaWiki 1.19, as well as the state-of-art APIs of MediaWiki (i.e. 1.34-wmf, as in 2019-05). Other versions in between are hopefully also compatible.

> For migrated FANDOM sites on MW 1.33+ (See [UCP migration post](https://community.fandom.com/wiki/User_blog:MisterWoodhouse/The_first_migrations_to_the_Unified_Community_Platform)), you may need to use Bot Password in order to login successfully.
> If you are on prior versions of .NET Core or .NET Framework, you may use prior version of the WCL packages. WCL v0.7.x libraries target .NET Standard 1.1 and should support lower versions of the .NET implementations.
> If you are on prior versions of .NET Core or .NET Framework, you may use prior version of the WCL packages.
> * WCL v0.7.x libraries target .NET Standard 1.1 and has dependency on `Newtonsoft.Json`
> * WCL v0.8.x libraries target .NET Standard 2.1, .NET 6.0 & .NET 8.0, and has dependency on `Newtonsoft.Json`
The packages `CXuesong.MW.WikiClientLibrary.*` are now available on NuGet. E.g. you may install the main package using the following command

Expand Down
8 changes: 0 additions & 8 deletions WikiClientLibrary.Commons/WikiClientLibrary.Commons.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,4 @@
<!-- Normalize value -->
<WCL_IS_CI_BUILD>True</WCL_IS_CI_BUILD>
</PropertyGroup>

<PropertyGroup Condition=" $([System.Text.RegularExpressions.Regex]::IsMatch($(TargetFramework), `^(netstandard2\.[2-9]|net(coreapp)?[3-9]\.\d+)$`)) ">
<DefineConstants>$(DefineConstants);BCL_FEATURE_EXECUTION_CONTEXT_RESTORE;BCL_FEATURE_MEMBER_NOT_NULL_ATTR</DefineConstants>
</PropertyGroup>

<PropertyGroup Condition=" $([System.Text.RegularExpressions.Regex]::IsMatch($(TargetFramework), `^net(coreapp)?[5-9]\.\d+$`)) ">
<DefineConstants>$(DefineConstants);BCL_FEATURE_RECORD</DefineConstants>
</PropertyGroup>
</Project>
8 changes: 4 additions & 4 deletions WikiClientLibrary.Commons/WikiClientLibrary.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@

<PropertyGroup>
<!-- Here are the default targets. Projects may override on this value with higher requirements. -->
<TargetFrameworks>netstandard2.1;net6.0;net8.0</TargetFrameworks>
<Version>0.8.0</Version>
<AssemblyVersion>0.8.0.10</AssemblyVersion>
<FileVersion>0.8.0.10</FileVersion>
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<Version>0.9.0-int.0</Version>
<AssemblyVersion>0.9.0.0</AssemblyVersion>
<FileVersion>0.9.0.0</FileVersion>
<Copyright>Copyright (C) CXuesong 2024</Copyright>
<PackageReleaseNotes>See https://github.com/CXuesong/WikiClientLibrary/releases .</PackageReleaseNotes>
<PackageTags>MediaWiki API Client</PackageTags>
Expand Down
89 changes: 0 additions & 89 deletions WikiClientLibrary/Infrastructures/ExecutionContextStash.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;

namespace WikiClientLibrary.Infrastructures
Expand Down Expand Up @@ -42,94 +36,11 @@ public void RestoreExecutionContext()
{
if (executionContext == null) return;
// Restore execution context inline.
#if BCL_FEATURE_EXECUTION_CONTEXT_RESTORE
// On .NET 5+, restores execution context with public API.
ExecutionContext.Restore(executionContext);
#else
// Otherwise, invoke our hacky delegate.
restoreExecutionContext(executionContext);
#endif
}

/// <summary>Calls <see cref="RestoreExecutionContext"/>.</summary>
public void Dispose() => RestoreExecutionContext();

#if !BCL_FEATURE_EXECUTION_CONTEXT_RESTORE
private static readonly Action<ExecutionContext> restoreExecutionContext = BuildRestoreExecutionContextDelegate();

private static Action<ExecutionContext> BuildRestoreExecutionContextDelegate()
{
// You feel like you're going to have a bad time.

// On .NET Standard 2.1, there is no ExecutionContext.Restore(ExecutionContext) public API.
// Cannot leverage ExecutionContext.Run and Awaiter.OnComplete impl here as CPS transformation done by C# compiler does not give us enough freedom.
// `AsyncTaskMethodBuilder<TResult>.GetStateMachineBox` is always capturing current ExecutionContext and
// restoring it *inside* OnComplete callback thunk. This behavior cannot be disabled.

// 1) Try to retrieve public/internal API (if exists) during runtime.
// Mono 6.4 https://github.com/mono/mono/blob/mono-6.4.0.198/mcs/class/referencesource/mscorlib/system/threading/executioncontext.cs#L117
var restoreMethod = typeof(ExecutionContext).GetMethod("Restore",
BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, CallingConventions.Any,
new[] { typeof(ExecutionContext) },
null);
if (restoreMethod != null)
{
var dm = new DynamicMethod("proxy$ExecutionContext.Restore",
typeof(void), new[] { typeof(ExecutionContext) },
typeof(ExecutionContextStash),
skipVisibility: true);
var gen = dm.GetILGenerator();
gen.Emit(OpCodes.Ldarg_0);
gen.EmitCall(OpCodes.Call, restoreMethod, null);
gen.Emit(OpCodes.Ret);

return (Action<ExecutionContext>)dm.CreateDelegate(typeof(Action<ExecutionContext>));
}

// ExecutionContext.Restore does not exist.
// 2) Try to leverage non-public API RestoreChangedContextToThread.
// .NET Core 3.x.
var restoreChangedContextToThreadMethod = typeof(ExecutionContext).GetMethod("RestoreChangedContextToThread",
BindingFlags.Static | BindingFlags.NonPublic, null, CallingConventions.Any,
new[] { typeof(Thread), typeof(ExecutionContext), typeof(ExecutionContext) },
null);
if (restoreChangedContextToThreadMethod != null)
{
var dm = new DynamicMethod("proxy$ExecutionContext.RestoreChangedContextToThread",
typeof(void), new[] { typeof(ExecutionContext) },
typeof(ExecutionContextStash),
skipVisibility: true);
var gen = dm.GetILGenerator();
var label1 = gen.DefineLabel();
var currentContextLocal = gen.DeclareLocal(typeof(ExecutionContext));

// currentContext = ExecutionContext.Capture()
gen.EmitCall(OpCodes.Call, typeof(ExecutionContext).GetMethod(nameof(ExecutionContext.Capture), Array.Empty<Type>()), null);
gen.Emit(OpCodes.Dup);
gen.Emit(OpCodes.Stloc, currentContextLocal.LocalIndex);
// if (currentContext != arg0) {
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Beq_S, label1);

// RestoreChangedContextToThread(Thread currentThread, ExecutionContext? contextToRestore, ExecutionContext? currentContext)
gen.EmitCall(OpCodes.Call, typeof(Thread).GetProperty(nameof(Thread.CurrentThread)).GetMethod, null);
gen.Emit(OpCodes.Ldarg_0);
gen.Emit(OpCodes.Ldloc, currentContextLocal.LocalIndex);
gen.EmitCall(OpCodes.Call, restoreChangedContextToThreadMethod, null);
gen.Emit(OpCodes.Ret);

// }
gen.MarkLabel(label1);
gen.Emit(OpCodes.Ret);

return (Action<ExecutionContext>)dm.CreateDelegate(typeof(Action<ExecutionContext>));
}

// We can do nothing to help. Sorry.
throw new PlatformNotSupportedException("Your current .NET CLR does not support restoring ExecutionContext. " +
"Please file an issue on CXuesong/WikiClientLibrary on github.");
}
#endif

}
}
58 changes: 6 additions & 52 deletions WikiClientLibrary/Pages/WikiPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -859,11 +859,7 @@ static PagePropertyCollection()

}

#if BCL_FEATURE_RECORD
public record WikiPageEditOptions
#else
public class WikiPageEditOptions
#endif
{

/// <summary>
Expand All @@ -876,74 +872,32 @@ public class WikiPageEditOptions
/// <para>When using with <see cref="WikiPage.AddSectionAsync"/>, the content does not include
/// the section heading part.</para>
/// </remarks>
public string Content
{
#if BCL_FEATURE_RECORD
get; init;
#else
get; set;
#endif
} = "";
public string Content { get; init; } = "";

/// <summary>
/// The edit summary. Leave it as <c>null</c> or empty string (<c>""</c>) to use the default edit summary.
/// </summary>
public string? Summary
{
#if BCL_FEATURE_RECORD
get; init;
#else
get; set;
#endif
}
public string? Summary { get; init; }

/// <summary>
/// Whether the edit is a minor edit. (See <a href="https://meta.wikimedia.org/wiki/Help:Minor_edit">m:Help:Minor Edit</a>)
/// </summary>
public bool Minor
{
#if BCL_FEATURE_RECORD
get; init;
#else
get; set;
#endif
}
public bool Minor { get; init; }

/// <summary>
/// Whether to mark the edit as bot; even if you are using a bot account the edits will not be marked unless you set this flag.
/// </summary>
public bool Bot
{
#if BCL_FEATURE_RECORD
get; init;
#else
get; set;
#endif
}
public bool Bot { get; init; }

/// <summary>
/// Specify how the watchlist is affected by this edit.
/// </summary>
public AutoWatchBehavior Watch
{
#if BCL_FEATURE_RECORD
get; init;
#else
get; set;
#endif
}
public AutoWatchBehavior Watch { get; init; }

/// <summary>
/// A token used to cancel the operation.
/// </summary>
public CancellationToken CancellationToken
{
#if BCL_FEATURE_RECORD
get; init;
#else
get; set;
#endif
}
public CancellationToken CancellationToken { get; init; }

}
}
12 changes: 0 additions & 12 deletions WikiClientLibrary/Utility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -269,16 +269,4 @@ public static Dictionary<TKey, TValue> ToDictionary<TKey, TValue>(this IEnumerab

}

#if !BCL_FEATURE_MEMBER_NOT_NULL_ATTR
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true, Inherited = false)]
internal sealed class MemberNotNullAttribute : Attribute
{
public MemberNotNullAttribute(string member) => this.Members = new[] { member };

public MemberNotNullAttribute(params string[] members) => this.Members = members;

public string[] Members { get; }
}
#endif

}

0 comments on commit 6655faa

Please sign in to comment.